Archived
1
0
Disbranĉigi 0
This commit is contained in:
Jaidyn Ann 2021-05-19 23:05:13 -05:00
commit a31d029e14
3 changed files with 893 additions and 0 deletions

23
README.md Normal file
View File

@ -0,0 +1,23 @@
# miid
miid's a simple IRC bot (hackily) written in shell.
It turns an irc room into a MUD/MUSH/IF-esque room, or at least it tries.
## Running
Use [ii](https://tools.suckless.org/ii/) to connect to your IRC server and
channel of choice― make sure to set up the nick and everything.
Then, you can just run miid every time the channel's output is updated:
$ miid irc/$IRC_NETWORK/$IRC_CHANNEL $BOT_NICK
It helps to have inotifywait installed, so this can be done automatically:
$ while inotifywait -e close_write irc/$IRC_NETWORK/$IRC_CHANNEL/out; \
do \
sh miid irc/$IRC_NETWORK/$IRC_CHANNEL/ $BOT_NICK; \
done
## Commands
Check the ./helpdoc.
## License
CC-0

44
helpdoc Normal file
View File

@ -0,0 +1,44 @@
I'm a MUSH/MUD/IF-esque bot, and you're a fleshbag. Nice to meetcha.
.
When I'm in a room, it's a literal room. You can look around, grab things,
use things, basically what you'd expect. You leave the room with /leave.
Please, try it. :-)
.
COMMANDS
――――――――――――――――――――――――――――――――――――――――
To use a command, type "/me COMMAND ARGS" or "ACTION COMMAND ARGS".
.
- examines/inspects/looks/x OBJECT
- examines/inspects/looks/x [at the room] [around] [here]
- takes/grabs/nabs OBJECT
- drops/removes/places OBJECT
- rummages [through sack] : List all items in your inventory
- rummages around : List all items in the room
.
BUILD COMMANDS
――――――――――――――――――――――――――――――――――――――――
You can create or edit objects, if you like! You can even program objects to
have something happen when "used", though for that you'll have to talk to
my admin.
.
- creates name : Creates an object and puts it in your inventory.
- aliases OBJECT as name : Adds an alias to the object's namelist.
- renames OBJECT as name : Renames object, and removes all old aliases.
- describes OBJECT as desc : Change it's description. Escape chars (\n) good.
- hinges OBJECT : Makes object openable, like a book.
- writes in/on OBJECT, text : Write on or in a given object.
- reads [in/on] OBJECT : Reads what is on/in an object.
.
- disowns OBJECT : Removes ownership, making the object communal
- permits USER with OBJECT : Allow another user to edit your object.
- gifts OBJECT to USER : Gift an object (and it's sole ownership)
.
BOT COMMANDS
――――――――――――――――――――――――――――――――――――――――
There are also some some commands that you issue to me as a message,
e.g., "narrator: help".
.
- pronouns [femme/masc]
- help
.
- END OF TRANSMISSION -

826
miid Normal file
View File

@ -0,0 +1,826 @@
#!/bin/sh
#―――――――――――――――――――――――――――――――――――――――
# name: miid, Mushy II Demon
# main: jadedctrl<@teknik.io>
# date: 2021
#―――――――――――――――――――――――――――――――――――――――
# ――――――――――――――――――――――――――――――――――――――
# MUSHY NONSENSE
# ――――――――――――――――――――――――――――――――――――――
function object_title {
local path="$1"
head -1 "${path}/name"
}
function object_description {
local path="$1"
cat "${path}/description"
}
function object_owner {
local path="$1"
cat "${path}/owner"
}
# Return whether or not object is owned by user
function object_owned {
local object="$1"
local user="$2"
local owner="$(object_owner "$object")"
if test -z "$owner"; then
return 0
elif echo "$owner" | grep "^${user}$" >/dev/null; then
return 0
else
return 1
fi
}
# get an object by name (in room, or inventory)
function get_object {
local user="$1"
local name="$2"
if echo "$name" | grep -e "^the " -e "^a " >/dev/null; then
name="$(chop_first "$name")"
fi
get_object_inventory "$user" "$name"
if test "$?" -eq 3; then
return;
fi
for item in "$IRC_DIR"/items/*; do
if grep -i "^$name" "${item}/name" >/dev/null; then
echo "${item}"
return
fi
done
}
function get_object_inventory {
local user="$1"
local name="$2"
local user_dir="${IRC_DIR}/users/${user}/"
for item in "$user_dir"/items/*; do
if grep -i "^$name" "${item}/name" >/dev/null; then
echo "${item}"
return 3
fi
done
}
# ――――――――――――――――――――――――――――――――――――――
# IRC
# ――――――――――――――――――――――――――――――――――――――
function pm {
local user="$1"
local message="$2"
echo "$message" \
| sed 's%^%/NOTICE '"$user"' :%g' \
> "${IRC_DIR}/in"
}
function privmsg {
local user="$1"
local message="$2"
echo "$message" \
| sed 's%^%/PRIVMSG '"$user"' :%g' \
> "${IRC_DIR}/in"
}
function msg {
local message="$1"
echo "$message" \
> "${IRC_DIR}/in"
}
function send_description {
local user="$1"
local obj_path="$2"
pm "$user" "$(object_description "$obj_path")"
}
# ――――――――――――――――――――――――――――――――――――――
# COMMANDS
# ――――――――――――――――――――――――――――――――――――――
# Print a description of object, user, or room
function examine_command {
local user="$1"
local args="$2"
# if examining the room
if test -z "$args"; then
send_description "$user" "$IRC_DIR"
rummage_command "$user" "around"
elif echo "$args" | grep "^at">/dev/null; then
examine_command "$user" "$(chop_first "$args")"
elif echo "around here" | grep "$args">/dev/null; then
send_description "$user" "$IRC_DIR"
rummage_command "$user" "around"
elif test "the room" = "$args"; then
send_description "$user" "$IRC_DIR"
rummage_command "$user" "around"
else
examine_object "$user" "$args"
fi
}
function examine_object {
local user="$1"
local name="$2"
local object="$(get_object "$user" "$name")"
if test -z "$object"; then
msg "There's nothing to look at, but $user sure is trying."
else
send_description "$user" "$object"
fi
}
function reads_command {
local user="$1"
local args="$2"
local preposition="$(first "$args")"
# "reads on"
if test "$preposition" = "on"; then
reads_on "$user" "$(get_object "$user" "$(chop_first "$args")")"
return
elif test "$preposition" = "in"; then
reads_in "$user" "$(get_object "$user" "$(chop_first "$args")")"
return
fi
local object="$(get_object "$user" "$args")"
if test -z "$object"; then
msg "$user squints $(their "$user") eyes, but can't see anything."
else
local openfile="${object}/open"
if is_hinged "$object"; then
if is_open "$object"; then
reads_in "$user" "$object"
else
reads_on "$user" "$object"
fi
fi
fi
}
function inscription {
local object="$1"
if test -f "${object}/inscription"; then
cat "${object}/inscription"
fi
}
function exscription {
local object="$1"
if test -f "${object}/exscription"; then
cat "${object}/exscription"
fi
}
function hinges_command {
echo "HINGING"
local user="$1"
local args="$2"
local object="$(get_object "$user" "$args")"
if test -z "$object"; then
msg "$user tries to make openable… something that doesn't exist."
else
echo "closed" \
> "${object}/open"
pm "$user" "Alright, now it's openable. Cool."
fi
}
function opens_command {
local user="$1"
local args="$2"
local object="$(get_object "$user" "$args")"
if test -z "$object"; then
msg "$user flails their opens in vain."
elif is_hinged "$object"; then
if is_open "$object"; then
pm "$user" "It's… it's already open."
else
echo "open" \
> "${object}/open"
fi
else
msg "$user tries to pry something open, to no avail."
fi
}
function closes_command {
local user="$1"
local args="$2"
local object="$(get_object "$user" "$args")"
if test -z "$object"; then
msg "$user flails their opens in vain."
elif is_hinged "$object"; then
if is_open "$object"; then
echo "close" \
> "${object}/open"
else
pm "$user" "Err… it's closed already. Are you OK?"
fi
else
msg "$user tries to cram something shut, but just hurts $(their "$user") fingers."
fi
}
function is_hinged {
local object="$1"
test -f "${object}/open"
return "$?"
}
function is_open {
local object="$1"
local openfile="${object}/open"
if test -f "$openfile" -a "$(cat "$openfile")" = "open"; then
return 0
fi
return 2;
}
function reads_in {
local user="$1"
local object="$2"
if is_open "$object"; then
local intext="$(inscription "$object")"
if test -z "$intext"; then
pm "$user" "Hm, it doesn't look like anything's written in it."
else
pm "$user" "$intext"
fi
else
pm "$user" "You have to open it first, dude…"
fi
}
function reads_on {
local user="$1"
local object="$2"
local outtext="$(exscription "$object")"
if test -z "$outtext"; then
pm "$user" "It doesn't look like anything's written on it."
else
pm "$user" "$outtext"
fi
}
function writes_command {
local user="$1"
local args="$2"
local preposition="$(first "$args")"
echo "WE WRITEEEEE"
# "reads on"
if test "$preposition" = "on"; then
writes_on "$user" "$(chop_first "$args")"
return
elif test "$preposition" = "in"; then
writes_in "$user" "$(chop_first "$args")"
return
fi
local object="$(get_object "$user" "$args")"
if test -z "$object"; then
msg "$user grips $(their "$user") pen and furiously writes air-letters."
else
local openfile="${object}/open"
if test -f "${object}/open"; then
if test "$(cat "$openfile")" = "open"; then
writes_in "$user" "$args"
else
writes_on "$user" "$args"
fi
fi
fi
}
function writes_in {
local user="$1"
local args="$2"
echo "$args ...vs... $(first "$args" ', ')"
local object="$(get_object "$user" "$(first "$args" ', ')")"
local text="$(chop_first "$args" ', ')"
if test -z "$object"; then
msg "$user grips $(their "$user") pen and frenetically writes air-letters."
elif is_open "$object"; then
printf "$text" \
> "${object}/inscription"
pm "$user" "You've written in it. What a scribe you are!"
else
pm "$user" "You can't write _in_ something if it's not open…"
fi
}
function writes_on {
echo " WRITEEEEE ON"
local user="$1"
local args="$2"
local object="$(get_object "$user" "$(first "$args" ', ')")"
local text="$(chop_first "$args" ', ')"
if test -z "$object"; then
msg "$user grips $(their "$user") pen and frenetically writes air-letters."
elif is_open "$object"; then
pm "$user" "You can't write on something if it's open…"
else
pritnf "$text" \
> "${object}/exscription"
pm "$user" "You've written in it. What a scribe you are!"
fi
}
# Take an object, put it in user's inventory
function take_command {
local user="$1"
local args="$2"
local object="$(get_object "$user" "$name")"
local objectname="$(object_title "$object")"
local userdir="${IRC_DIR}/users/${user}/items"
if test -z "$object"; then
msg "$user suddenly throws $(their "$user") arms forward, for some reason."
else
graveyard_store "$object"
mv "$object" "${userdir}/"
pm "$user" "You hurriedly put the ${objectname} into your pocket."
fi
}
# Move an object from inventory into the room
function drop_command {
local user="$1"
local args="$2"
local object="$(get_object_inventory "$user" "$name")"
local objectname="$(object_title "$object")"
if test -z "$object"; then
msg "${user} rummages through $(their) pockets. $(They "$user") looks silly."
else
graveyard_store "$object"
mv "$object" "${IRC_DIR}/items/"
pm "$user" "You put down a $objectname from your pocket."
fi
}
# Prints every item in the user's inventory.
function rummage_command {
local user="$1"
local args="$2"
local topdir="${IRC_DIR}/users/$user"
local message="In your sack, you find:"
if test "$args" = "around"; then
topdir="${IRC_DIR}"
message="Here you can see:"
fi
local item_list=""
for item in "$topdir"/items/*; do
if echo "$item" | grep '\*'; then
echo "Not item."
elif test -z "$item_list"; then
item_list="A $(object_title "$item")"
else
item_list="$item_list, a $(object_title "$item")"
fi
done
if test -z "$item_list"; then
pm "$user" "You can't see anything."
else
pm "$user" "${message} ${item_list}."
fi
}
# ――――――――――――――――――――――――――――――――――――――
# BUILD COMMANDS
# ――――――――――――――――――――――――――――――――――――――
function creates_command {
local user="$1"
local args="$2"
local name="$args"
if echo "$name" | grep "^a " >/dev/null; then
name="$(chop_first "$name")"
fi
local filename="$(first "$name")-$(date +%m%d%H%M)"
local itemdir="${IRC_DIR}/users/${user}/items/${filename}"
mkdir -p "$itemdir"
echo ""\
> "${itemdir}/description"
echo "${name}" \
> "${itemdir}/name"
echo "${user}" \
> "${itemdir}/owner"
pm "$user" "You magick a $name into existence, and cram it into your favourite pocket."
}
function aliases_command {
local user="$1"
local args="$2"
local objectquery="$(first "$args" ' as ')"
local object="$(get_object "$user" "$objectquery")"
local objectname="$(object_title "$object")"
local objectowner="$(object_owner "$object")"
if test -z "$object"; then
msg "${user} mumbles under $(their "$user") breath about something or another."
elif object_owned "$object" "$user"; then
echo "$(chop_first "$args" ' as ')" \
>> "${object}/name"
pm "$user" "You've given the $objectname a new name, $args."
else
msg "${user} tries in vain to give a name to the $objectname of $objectowner."
fi
}
function renames_command {
local user="$1"
local args="$2"
local objectquery="$(first "$args" ' as ')"
local object="$(get_object "$user" "$objectquery")"
local objectname="$(object_title "$object")"
local objectowner="$(object_owner "$object")"
if test -z "$object"; then
msg "${user} looks around, making vague noises of confusion."
elif object_owned "$object" "$user"; then
echo "$(chop_first "$args" ' as ')" \
> "${object}/name"
pm "$user" "You hath stoleth and regiven to $objectname."
else
msg "${user} tries in vain to steal name from the $objectname of $objectowner."
fi
}
function describes_command {
local user="$1"
local args="$2"
local objectquery="$(first "$args" ' as ')"
if test "$objectquery" = "here" -o "$objectquery" = "this room"; then
describes_room "$user" "$args"
return
fi
# else, it's an object.
local object="$(get_object "$user" "$objectquery")"
local objectname="$(object_title "$object")"
local objectowner="$(object_owner "$object")"
if test -z "$object"; then
msg "${user} looks around, making vague noises of confusion."
elif object_owned "$object" "$user"; then
printf "$(chop_first "$args" ' as ')" \
> "${object}/description"
pm "$user" "Your pen has changed the appearance of $objectname."
else
msg "${user} reallyy wants to use their new pen, but $objectowner won't allow it."
fi
}
function describes_room {
local user="$1"
local args="$2"
local desc="$(chop_first "$args" ' as ')"
if test -n "$desc"; then
printf "$desc" \
> "${IRC_DIR}/description"
pm "$user" "Your pen has changed the appearance of the room."
else
msg "${user} reallyy wants to use their new pen, but they've nothing to say."
fi
}
function permits_command {
local user="$1"
local args="$2"
local new_owner="$(first "$args")"
local object="$(get_object "$user" "$(chop_first "$args" ' with ')")"
local objectname="$(object_title "$object")"
local objectowner="$(object_owner "$object")"
if test -z "$object"; then
msg "${user} raises their hand authoritatively, but has nothing to say."
elif object_owned "$object" "$user"; then
if test -z "$objectowner"; then
pm "$user" "This is already communal property, you reactionary."
return
fi
echo "$new_owner" \
>> "${object}/owner"
pm "$user" "You've allowed $new_owner to much around with it."
pm "$new_owner" "$user has kindly allowed you to heck around with the $objectname."
else
msg "${user} is eager to give away what isn't owned by $(them "$user")."
fi
}
function gifts_command {
local user="$1"
local args="$2"
local new_owner="$(chop_first "$args" ' to ')"
local object="$(get_object "$user" "$(first "$args" ' to ')")"
local objectname="$(object_title "$object")"
local objectowner="$(object_owner "$object")"
if test -z "$object"; then
msg "${user} warmly extends $(their "$user") hands… but they're empty."
elif object_owned "$object" "$user"; then
if test -z "$objectowner"; then
pm "$user" "This is already communal property, you reactionary."
elif test "$(wc -l "${object}/owner" | awk '{print $1}')" -gt 1; then
pm "$user" "You can't gift something also owned by others."
else
echo "$new_owner" \
> "${object}/owner"
pm "$user" "You've given it to $new_owner, hurrah! Sharing."
pm "$new_owner" "The ${objectname} is now in your inventory."
ensure_user "$new_owner"
mv "${object}" "${IRC_DIR}/users/${new_owner}/items/"
fi
else
msg "${user} is a fan of regifting, but $objectowner isn't hearing it."
fi
}
# ――――――――――――――――――――――――――――――――――――――
# BOT COMMANDS
# ――――――――――――――――――――――――――――――――――――――
function help_command {
local user="$1"
local args="$2"
msg "$user: Fine, I'll PM you my helpdoc."
if test -f helpdoc; then
privmsg "$user" "$(cat helpdoc)"
else
privmsg "$user" "Uhhh… there's no helpdoc? My admin hecked up."
fi
}
function pronouns_command {
local user="$1"
local args="$2"
local pronouns="${IRC_DIR}/users/${user}/pronouns"
if test "$(first "$args")" = "masc"; then
printf "he\nhim\nhis\nMr.\nsir\n" > "$pronouns"
pm "${user}" "Masc it is then, nerd."
elif test "$(first "$args")" = "femme"; then
printf "she\nher\nher\nMs.\nma'am\n" > "$pronouns"
pm "${user}" "Alright, your pronouns are femme now. Nerd."
else
printf "they\nthem\ntheir\nMx.\nmir\n" > "$pronouns"
pm "${user}" "Alright, your pronouns are they as heck."
fi
}
# ――――――――――――――――――――――――――――――――――――――
# PRONOUNS
# ――――――――――――――――――――――――――――――――――――――
function they {
local user="$1"
head -1 "${IRC_DIR}/users/${user}/pronouns"
}
function They { cap_first "$(they "$1")"; }
function them {
local user="$1"
head -2 "${IRC_DIR}/users/${user}/pronouns" | tail -1
}
function Them { cap_first "$(them "$1")"; }
function their {
local user="$1"
head -3 "${IRC_DIR}/users/${user}/pronouns" | tail -1
}
function Their { cap_first "$(their "$1")"; }
function Mx {
local user="$1"
head -4 "${IRC_DIR}/users/${user}/pronouns" | tail -1
}
function mir {
local user="$1"
tail -1 "${IRC_DIR}/users/${user}/pronouns"
}
function Mir { cap_first "$(mir "$1")"; }
# ――――――――――――――――――――――――――――――――――――――
# UTIL FUNCTIONS
# ――――――――――――――――――――――――――――――――――――――
# Gets first word of string
function first {
local string="$1"
local delimeter="$2"
if test -z "$delimeter"; then delimeter=' '; fi
echo "$string" \
| awk -F "$delimeter" '{ print $1 }'
}
# Removes first word from string
function chop_first {
local word="$1"
local delimeter="$2"
if test -z "$delimeter"; then delimeter=' '; fi
echo "$word" \
| awk -F "$delimeter" '{ $1=""; print $0 }' \
| sed 's%^ %%'
}
# Capitalize the first character of given string
function cap_first {
local string="$1"
local first="$(echo "$string" | cut -c1 | tr '[a-z]' '[A-Z]')"
local second="$(echo "$string" | cut -c2-)"
echo "${first}${second}"
}
# Save an object before a write operation, justtt in case
function graveyard_store {
local file="$1"
local graveyard="${IRC_DIR}/graveyard/$(date +%Y-%m-%d)"
mkdir -p "$graveyard"
cp -r "$file" "$garveyard"
}
# ――――――――――――――――――――――――――――――――――――――
# PARSER HELPER FUNCTIONS
# ――――――――――――――――――――――――――――――――――――――
function is_membership_line {
grep '....-..-.. ..:.. -!-' > /dev/null
}
# Parse a message line
function message_user {
echo "$1" \
| sed 's%....-..-.. ..:.. <%%' \
| awk -F '>' '{ print $1 }'
}
function message_text {
echo "$1" \
| sed 's%....-..-.. ..:.. <%%' \
| awk -F '>' '{ $1=""; print $0 }' \
| sed 's%^ %%' \
| tr -d '\001'
}
# Parse a membership update line (-!-)
function membership_user {
echo "$1" \
| sed 's%....-..-.. ..:.. -!- %%' \
| sed 's%(.*).*%%'
}
function membership_type {
echo "$1" \
| sed 's%....-..-.. ..:.. -!- %%' \
| sed 's%.*(.*) has %%' \
| sed 's% .*%%'
}
# ――――――――――――――――――――――――――――――――――――――
# PARSING
# ――――――――――――――――――――――――――――――――――――――
function command_parse {
local user="$1"
local command="$2"
local args="$3"
if test "$command" = "ACTION"; then
command_parse "$user" "$(echo "$args" | awk '{print $1}')" \
"$(chop_first "$args")"
elif test "$command" = "${NICK}:"; then
bot_command_parse "$user" "$(first "$args")" \
"$(chop_first "$args")"
elif echo "examines inspects x looks" | grep "$command">/dev/null ; then
examine_command "$user" "$args"
elif echo "takes grabs nabs" | grep "$command">/dev/null ; then
take_command "$user" "$args"
elif echo "drops removes places" | grep "$command">/dev/null ; then
drop_command "$user" "$args"
elif test "$command" = "rummages"; then
rummage_command "$user" "$args"
elif test "$command" = "creates"; then
creates_command "$user" "$args"
elif test "$command" = "aliases"; then
aliases_command "$user" "$args"
elif test "$command" = "renames"; then
renames_command "$user" "$args"
elif test "$command" = "describes"; then
describes_command "$user" "$args"
elif test "$command" = "permits"; then
permits_command "$user" "$args"
elif test "$command" = "gifts"; then
gifts_command "$user" "$args"
elif test "$command" = "hinges"; then
hinges_command "$user" "$args"
elif test "$command" = "opens"; then
opens_command "$user" "$args"
elif test "$command" = "closes"; then
closes_command "$user" "$args"
elif test "$command" = "writes"; then
writes_command "$user" "$args"
elif test "$command" = "reads"; then
reads_command "$user" "$args"
fi
}
function bot_command_parse {
local user="$1"
local command="$2"
local args="$3"
if test "$command" = "help"; then
help_command "$user" "$args"
elif test "$command" = "pronouns"; then
pronouns_command "$user" "$args"
fi
}
function message_parse {
local line="$1"
local text="$(message_text "$line")"
local command="$(echo "$text" | awk '{ print $1 }')"
local args="$(chop_first "$text")"
command_parse "$(message_user "$line")" "$command" "$args"
}
function membership_parse {
local line="$1"
if test "$(membership_type "$line")" = "joined"; then
# send_description "$(membership_user "$line")" "$IRC_DIR"
echo "Member entered."
fi
}
function ensure_user {
local user="$1"
local userdir="${IRC_DIR}/users/${user}"
local descfile="${userdir}/description"
local pronounfile="${userdir}/pronouns"
mkdir -p "${userdir}/items"
if test ! -f "$pronounfile"; then
printf "they\nthem\ntheir\nMx.\nmir\n" > "$pronounfile"
fi
if test ! -f "${descfile}"; then
echo "$user looks like a fairly regular person." \
>> "$descfile"
echo "$(They "$user") look at you, tilting their head a bit." \
>> "$descfile"
fi
}
function parse_line {
local line="$1"
if echo "$line" | is_membership_line; then
ensure_user "$(membership_user "$line")"
mkdir -p "${IRC_DIR}/users/$(membership_user "$line")/items"
membership_parse "$line"
else
ensure_user "$(message_user "$line")"
mkdir -p "${IRC_DIR}/users/$(message_user "$line")/items"
message_parse "$line"
fi
}
# ――――――――――――――――――――――――――――――――――――――
# INVOCATION
# ――――――――――――――――――――――――――――――――――――――
# The irc directory for a given room, as created by ii (suckless).
# e.g., irc/irc.freenode.org/#miid/
IRC_DIR="$1"
NICK="$2"
if test -z "$NICK"; then
echo "usage: $0 II_ROOM_DIR NICK"
# exit 2;
else
parse_line "$(tail -1 "${IRC_DIR}/out")" "$NICK"
fi