toxio

Turn (almost) any CLI program into a Tox bot
git clone git://git.luxferre.top/toxio.git
Log | Files | Refs | README | LICENSE

toxio.nim (2842B)


      1 # toxio: turn any CLI program/script into a Tox bot
      2 # deps: toxcore, nim-toxcore
      3 # created by Luxferre in 2024, released into public domain
      4 
      5 import toxcore, times, sets, osproc, streams
      6 from threadpool import spawn
      7 from std/os import sleep
      8 from std/cmdline import paramCount, paramStr
      9 
     10 # command line parameters
     11 if paramCount() < 4:
     12   echo "Usage: toxio name datafile [w|r] cmd"
     13   quit(1)
     14 let
     15   botname = paramStr(1)
     16   toxdatafile = paramStr(2)
     17   writeperm: bool = (paramStr(3) == "w")
     18   subcmd = paramStr(4)
     19 
     20 # read Tox save data
     21 var toxconfig = ""
     22 try:
     23   toxconfig = readFile(toxdatafile)
     24 except IOError:
     25   stderr.writeLine "Cannot read the Tox data file!"
     26   quit(1)
     27 
     28 # bootstrap constants
     29 const
     30   bootstrapHost = "46.101.197.175"
     31   bootstrapKey = "CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707".toPublicKey
     32   bootstrapPort = 33445
     33 
     34 # declare the bot type to wrap GC-safe data
     35 type Bot = ref object
     36   tox: Tox
     37   datafile: string
     38   activeFriends: HashSet[Friend]
     39   sproc: Process
     40 
     41 # input loop procedure
     42 proc inputloop(bot: Bot) =
     43   var gline = ""
     44   while bot.sproc.outputStream.readLine(gline):
     45     for f in bot.activeFriends.items:
     46       bot.tox.send(f, gline, TOX_MESSAGE_TYPE_NORMAL)
     47   bot.sproc.outputStream.flush()
     48 
     49 # GC-safe bot callbacks
     50 proc cbs(bot: Bot) = 
     51   # what do do on a new friend request (only in write-enable mode)
     52   if writeperm:
     53     bot.tox.onFriendRequest do (pk: PublicKey; msg: string):
     54       discard bot.tox.addFriendNoRequest(pk)
     55       try:
     56         writeFile(bot.datafile, bot.tox.saveData)
     57       except IOError:
     58         stderr.writeLine "Cannot write the Tox data file!"
     59 
     60   # what to do on a new message
     61   bot.tox.onFriendMessage do (f: Friend; msg: string; kind: MessageType):
     62     if msg == "/toxiostop": # temporarily unsubscribe from the output
     63       bot.activeFriends.excl(f)
     64     else:
     65       bot.activeFriends.incl(f) # first-time activation
     66       bot.sproc.inputStream.writeLine msg
     67       bot.sproc.inputStream.flush()
     68 
     69 # bot options description
     70 proc botopt(opts: Options) =
     71   opts.savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE
     72   opts.saveData = toxconfig
     73 
     74 # spawn the subprocess
     75 var subproc:Process
     76 
     77 try:
     78   subproc = startProcess(subcmd, options={poEvalCommand,poStdErrToStdOut,poInteractive,poUsePath})
     79   stderr.writeLine "Subprocess started"
     80 except:
     81   stderr.writeLine "Cannot spawn the subprocess command!"
     82   quit(1)
     83 
     84 # init the bot
     85 let iobot = Bot(tox:initTox(botopt))
     86 iobot.tox.name = botname
     87 iobot.datafile = toxdatafile
     88 iobot.cbs()
     89 iobot.tox.bootstrap(bootstrapHost, bootstrapKey, bootstrapPort)
     90 iobot.tox.statusMessage = "Serving " & subcmd
     91 iobot.sproc = subproc
     92 stderr.writeLine iobot.tox.name, " online at ", iobot.tox.address
     93 
     94 # run the input loop
     95 spawn inputloop(iobot)
     96 
     97 # run the toxcore loop
     98 while true:
     99   iterate(iobot.tox)
    100   sleep inMilliseconds(iobot.tox.iterationInterval)