Compare commits

..

5 Commits

Author SHA1 Message Date
Stef Dunlap cb2d5aa868 Kill off any forked processes on timeout
Previously we bwrap-ed the whole our.rb script. In this commit we switch
it so that our.rb is run outside of bwrap, but every user command it
executes is done inside bwrap. This allows us to use bwrap's
"--die-with-parent" (along with "--unshare-pid") to kill off any forked
processes when the parent processes is killed due to a timeout.
2023-02-11 17:39:31 -05:00
Stef Dunlap fb94656f49 Timeout commands if they run for longer than three seconds 2023-02-11 17:39:31 -05:00
Stef Dunlap e6d32c5cab Add persistent storage in CMDS_DIR/data 2023-02-11 17:39:31 -05:00
Stef Dunlap 47425fdd85 Customizable start up params 2023-02-11 17:39:31 -05:00
Stef Dunlap 3ffba8f981 initial dzwdz commit 2023-02-11 17:39:31 -05:00
34 changed files with 445 additions and 0 deletions

3
.envrc 100644
View File

@ -0,0 +1,3 @@
export OUR_NICK=your
export OUR_CHANNELS='#bots'
export OUR_CMDS_DIR=/town/our

1
boy 100644
View File

@ -0,0 +1 @@
nyaa~~

BIN
cmds/.tw.un~ 100755

Binary file not shown.

6
cmds/arguments 100755
View File

@ -0,0 +1,6 @@
#!/bin/sh
echo -n "\$1 => " ; test -z "$1" && echo -n "<empty> | " || echo -n "$1 | "
echo -n "\$2 => " ; test -z "$2" && echo -n "<empty> | " || echo -n "$2 | "
echo -n "\$3 => " ; test -z "$3" && echo -n "<empty> | " || echo -n "$3 | "
echo -n "\$4 => " ; test -z "$4" && echo -n "<empty> | " || echo -n "$4 | "

9
cmds/count 100755
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
DIR=$(dirname -- $0)
DATA="$DIR/data/count"
old_number=$(cat "$DATA")
new_number=$((old_number + 1))
echo $new_number > $DATA
echo $new_number

2
cmds/data/.gitignore vendored 100644
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
cmds/echo 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
echo $1

16
cmds/fruit 100755
View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
import random
from io import StringIO
base = 0x1f345
nSides = 14
rounds = 14
fp = StringIO()
for i in range(rounds):
roll = random.randint(0, nSides)
chcr = base + roll
fp.write(chr(chcr))
print(fp.getvalue())

2
cmds/greet 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
echo hi $2 from $3

6
cmds/help 100755
View File

@ -0,0 +1,6 @@
#!/usr/bin/env python3
import sys
if len(sys.argv) > 1 and sys.argv[1] == "writing":
print("you can add new scripts to our by placing executable files in the /town/our directory. Make sure they have the +x permission. programs are passed these arguments by the bot: the arguments given in the message (our/[program] [args]), the user ID, and the channel it was run from.")
else:
print("the our bot allows smol programs written by townies to be used by typing \"our/[program]\" from IRC. See also \"our/help writing\"")

2
cmds/howami 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
echo "You're doing great."

17
cmds/jikan 100755
View File

@ -0,0 +1,17 @@
#!/usr/bin/env hy3
(import [datetime [datetime]])
(setv fullwidth (str.maketrans
(dfor [i ch] (enumerate "") [(str i) ch])))
(setv weekdays "日月火水木金土") (comment 曜日)
(defn ctime []
(setv d (datetime.now))
(-> (.format "{}年{}月{}日 {:02d}{:02d}{:02d}"
d.year d.month d.day d.hour d.minute d.second)
(.translate fullwidth)))
(print (ctime))

4
cmds/ls 100755
View File

@ -0,0 +1,4 @@
#!/bin/sh
DIR=$(dirname -- $0)
find $DIR/* -maxdepth 1 -perm -111 -type f -printf "%f "
echo

2
cmds/qotd 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
nc localhost 17 -W 1

3
cmds/rev 100755
View File

@ -0,0 +1,3 @@
#!/bin/sh
echo "$1"|rev

35
cmds/roll 100755
View File

@ -0,0 +1,35 @@
#!/usr/bin/env python3
# A simple dice rolling app
# Written by xaphania 2022-04-19
# v0.1.our
# modified for use with the our irc bot 2022-04-21
# yes this probably sucks. please send your flames to xaphania@tilde.town
import random
import sys
try:
dstring = sys.argv[1]
except:
dstring = "1d6"
try:
numDice = int(dstring.split("d",1)[0])
numFace = int(dstring.split("d",1)[1])
except:
print(f"{dstring} is not a valid dice format")
exit()
diceList=[]
while numDice>0:
diceList.append(random.randint(1,numFace))
numDice-=1
total = sum(diceList)
avg = total / len(diceList)
high = max(diceList)
low = min(diceList)
print (f"Result: {diceList} | Total: {total} Average: {avg} Highest: {high} Lowest: {low}")

BIN
cmds/room 100755

Binary file not shown.

89
cmds/room.go 100644
View File

@ -0,0 +1,89 @@
package main
import (
"fmt"
"math/rand"
"time"
)
func randStr(s []string) string {
return s[rand.Intn(len(s))]
}
func airQuality() string {
return randStr([]string{
"moist",
"warm",
"humid",
"cool",
"dry",
"electric",
"dusty",
"hazy",
"smoky",
"clear",
})
}
func wallType() string {
return randStr([]string{
"bronze",
"mossy stone",
"wet stone",
"copper",
"tile",
"hard wood",
"stained wood",
"purple",
"light blue",
"yellowed",
"vine choked",
"art hung",
})
}
func lightQuality() string {
return randStr([]string{
"dimly lit",
"brightly lit",
"faintly lit",
"covered in dancing shadows",
"dappled",
})
}
func lightSource() string {
return randStr([]string{
"candle light",
"bioluminescent moss",
"bioluminescent fungus",
"lantern light",
"flickering monitors",
"a cracked LCD",
"a fireplace",
"a torch",
"sconces",
})
}
func aroma() string {
return randStr([]string{
"lavender",
"cloves",
"sulphur",
"baking cookies",
"dirt",
"wet soil",
"cut grass",
"wet dog",
"cinnamon",
"swamp",
"moss",
})
}
func main() {
rand.Seed(time.Now().UTC().UnixNano())
fmt.Printf("the air is %s. %s walls are %s by %s. it smells faintly of %s.\n",
airQuality(), wallType(), lightQuality(), lightSource(), aroma())
}

3
cmds/rot13 100755
View File

@ -0,0 +1,3 @@
#!/bin/sh
echo "$1"|rot13

2
cmds/sh 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
sh -c "$1"

11
cmds/time 100755
View File

@ -0,0 +1,11 @@
#!/bin/sh
#echo "arguments are 1:$1 2:$2 3:$3 4:$4"
args="$1";
user="$2";
channel="$3";
nick="$(echo "$user"|cut -d "!" -f 1)"
args_lower="$(echo "$args"|tr [:upper:] [:lower:])"
case "$args_lower" in
("to shine") echo "yo, $nick, 🌟💎shine bright like a diamond!💎🌟";;
esac

6
cmds/timefor 100755
View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
echo $2
ip=$(finger $1 | egrep -o -m 1 '([0-9]{1,3}\.){3}[0-9]{1,3}')
tz=$(curl -s "https://ipapi.co/$ip/timezone")
TZ=$tz date -R

5
cmds/tw 100755
View File

@ -0,0 +1,5 @@
#!/bin/bash
user="$2"
nick="$(echo "$user"|cut -d "!" -f 1)"
/home/jmjl/bin/tw -u $nick $1 | sed -z 's/\n/ - /g'
# /home/jmjl/bin/tw -u $nick $1

2
cmds/uptime 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
uptime

61
cmds/weather 100755
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
######################################################
# Takes a location, e.g. "New York" as an argument #
# and returns the current weather conditions. Uses #
# the weather data API courtesy of metaweather.com #
######################################################
######################################################
# Written by ~xaphania 2022-04-22 #
# For use on tilde.town with the 'our' IRC bot #
# Please send comments or suggestions via town mail #
# to xaphania@tilde.town #
# v0.1 #
# TO DO - customise weather strings, add emoji? #
# TO DO - more features: detailed reports, future #
# and past weather conditions, etc #
######################################################
import sys
import json
import urllib.request
# get argument, replace spaces with %20
search = sys.argv[1].replace(" ","%20")
# Get Where on Earth ID for specified location
searchUrl = "https://www.metaweather.com/api/location/search/?query=" + search
try:
locationData = json.loads(urllib.request.urlopen(searchUrl).read())
except:
print("Couldn't contact metaweather. Service may be down, please try later.")
exit()
try:
locationID = locationData[0]["woeid"]
except:
search = search.replace("%20"," ")
print(f"Sorry, I couldn't find location {search}")
exit()
# Get weather data for that woeid
weatherUrl = "https://www.metaweather.com/api/location/"+str(locationID)+"/"
try:
weatherData = json.loads(urllib.request.urlopen(weatherUrl).read())
except:
print("Couldn't contact metaweather. Service may be down, please try later.")
exit()
try:
state = weatherData["consolidated_weather"][0]["weather_state_name"]
temp = round(weatherData["consolidated_weather"][0]["the_temp"])
location = weatherData["title"]
parent = weatherData["parent"]["title"]
print(f"In {location}, {parent} it's {temp}°c and {state}" \
+" (Weather Data from www.metaweather.com)")
except:
print("Sorry, there was an error retreiving weather data. Please try later.")

2
cmds/whatami 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
echo "A human (most likely)"

2
cmds/whenami 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
date

2
cmds/whereami 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
echo $3

2
cmds/whoami 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
echo $2

2
cmds/whyami 100755
View File

@ -0,0 +1,2 @@
#!/bin/sh
echo "https://en.wikipedia.org/wiki/Meaning_of_life"

View File

@ -0,0 +1,18 @@
[Unit]
Description=our
After=our.service
[Service]
Type=simple
WorkingDirectory=/home/kindrobot/wrk/our
ExecStart=/home/kindrobot/wrk/our/our.rb
Environment="OUR_NICK=your"
Environment="OUR_CHANNELS=#bots"
Environment="OUR_CMDS_DIR=/town/our"
Restart=always
RestartSec=5
StartLimitInterval=60s
StartLimitBurst=3
[Install]
WantedBy=default.target

111
our.rb 100755
View File

@ -0,0 +1,111 @@
#!/usr/bin/env ruby
require 'open3'
require 'socket'
require 'timeout'
# configurable environment variables
nick = ENV['OUR_NICK'] || 'our'
channels = ENV['OUR_CHANNELS'] || '#tildetown,#bots'
prefix = ENV['OUR_PREFIX'] || "#{nick}/"
cmds_dir = ENV['OUR_CMDS_DIR'] || '/town/our'
module IRC
class User
attr_accessor :s
def initialize addr, port, nick
@hooks = []
@s = TCPSocket.open addr, port.to_s
s.puts "USER #{nick} fakehost whatevenisaservername :beep boop"
s.puts "NICK #{nick}"
hook do |m|
next unless m.cmd == 'PING'
raw "PING #{nick}"
end
end
def raw msg
@s.puts msg
end
def join chan
raw "JOIN #{chan}"
end
def privmsg target, msg
raw "PRIVMSG #{target} :#{msg}"
end
def hook &h
@hooks << h
end
def loop
while line = s.gets
msg = Message.new line
@hooks.each{|h| h.call(msg)}
end
end
end
class Message
attr_accessor :prefix, :cmd, :args, :raw
# TODO custom constructor
def initialize msg
msg = msg.delete_suffix "\r\n"
@raw = msg
@prefix = nil
@prefix, msg = msg[1..].split(' ', 2) if msg[0] == ':'
@cmd, msg = msg.split(' ', 2)
@args = []
while msg and not msg.empty?
if msg[0] == ':'
@args << msg[1..]
break
end
s, msg = msg.split(' ', 2)
@args << s
end
end
end
end
puts "starting"
i = IRC::User.new 'localhost', 6667, nick
channels.split(',').each { |channel| i.join channel }
i.hook do |msg|
next unless msg.cmd == 'PRIVMSG'
target, content = msg.args
next unless content.delete_prefix! prefix
cmd, args = content.split(' ', 2)
cmd = "#{cmds_dir}/#{cmd}"
args ||= ''
next unless File.exists? cmd
if not File.executable? cmd
i.privmsg target, "#{cmd} isn't executable. try chmod +x"
next
end
begin
Open3.popen2e("#{__dir__}/wrap_it.sh", cmd, args, msg.prefix, target) do |_, stdout, wait_thread|
out = nil
Timeout::timeout(3) do
out = stdout.gets # only interested in the first line of output
stdout.gets until stdout.eof? # make sure process finishes in time allotted
end
i.privmsg target, out if out
rescue Timeout::Error
Process.kill("KILL", wait_thread.pid)
i.privmsg target, "[our.rb] command timed out"
end
rescue Exception => e
i.privmsg target, "[our.rb] #{e.to_s}"
end
next true
end
i.loop

2
passwd 100644
View File

@ -0,0 +1,2 @@
root:x:0:0:root:/root:/bin/ed
weed:x:420:666:lmao:/weed:/bin/mount.ext4

15
wrap_it.sh 100755
View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
DIR=$(dirname -- $0)
OUR_CMDS_DIR=${OUR_CMDS_DIR:-/town/our}
/usr/bin/bwrap \
--unshare-all \
--ro-bind / / \
--bind "$OUR_CMDS_DIR/data" "$OUR_CMDS_DIR/data" \
--share-net \
--dev /dev \
--tmpfs /tmp \
--unshare-pid \
--die-with-parent \
"$@"