Files
toxio/toxio.nim
T
2024-01-04 15:43:00 +02:00

101 lines
2.8 KiB
Nim

# toxio: turn any CLI program/script into a Tox bot
# deps: toxcore, nim-toxcore
# created by Luxferre in 2024, released into public domain
import toxcore, times, sets, osproc, streams
from threadpool import spawn
from std/os import sleep
from std/cmdline import paramCount, paramStr
# command line parameters
if paramCount() < 4:
echo "Usage: toxio name datafile [w|r] cmd"
quit(1)
let
botname = paramStr(1)
toxdatafile = paramStr(2)
writeperm: bool = (paramStr(3) == "w")
subcmd = paramStr(4)
# read Tox save data
var toxconfig = ""
try:
toxconfig = readFile(toxdatafile)
except IOError:
stderr.writeLine "Cannot read the Tox data file!"
quit(1)
# bootstrap constants
const
bootstrapHost = "46.101.197.175"
bootstrapKey = "CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707".toPublicKey
bootstrapPort = 33445
# declare the bot type to wrap GC-safe data
type Bot = ref object
tox: Tox
datafile: string
activeFriends: HashSet[Friend]
sproc: Process
# input loop procedure
proc inputloop(bot: Bot) =
var gline = ""
while bot.sproc.outputStream.readLine(gline):
for f in bot.activeFriends.items:
bot.tox.send(f, gline, TOX_MESSAGE_TYPE_NORMAL)
bot.sproc.outputStream.flush()
# GC-safe bot callbacks
proc cbs(bot: Bot) =
# what do do on a new friend request (only in write-enable mode)
if writeperm:
bot.tox.onFriendRequest do (pk: PublicKey; msg: string):
discard bot.tox.addFriendNoRequest(pk)
try:
writeFile(bot.datafile, bot.tox.saveData)
except IOError:
stderr.writeLine "Cannot write the Tox data file!"
# what to do on a new message
bot.tox.onFriendMessage do (f: Friend; msg: string; kind: MessageType):
if msg == "/toxiostop": # temporarily unsubscribe from the output
bot.activeFriends.excl(f)
else:
bot.activeFriends.incl(f) # first-time activation
bot.sproc.inputStream.writeLine msg
bot.sproc.inputStream.flush()
# bot options description
proc botopt(opts: Options) =
opts.savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE
opts.saveData = toxconfig
# spawn the subprocess
var subproc:Process
try:
subproc = startProcess(subcmd, options={poEvalCommand,poStdErrToStdOut,poInteractive,poUsePath})
stderr.writeLine "Subprocess started"
except:
stderr.writeLine "Cannot spawn the subprocess command!"
quit(1)
# init the bot
let iobot = Bot(tox:initTox(botopt))
iobot.tox.name = botname
iobot.datafile = toxdatafile
iobot.cbs()
iobot.tox.bootstrap(bootstrapHost, bootstrapKey, bootstrapPort)
iobot.tox.statusMessage = "Serving " & subcmd
iobot.sproc = subproc
stderr.writeLine iobot.tox.name, " online at ", iobot.tox.address
# run the input loop
spawn inputloop(iobot)
# run the toxcore loop
while true:
iterate(iobot.tox)
sleep inMilliseconds(iobot.tox.iterationInterval)