initial upload

This commit is contained in:
Luxferre
2024-01-04 15:43:00 +02:00
commit 591928394f
4 changed files with 200 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
+15
View File
@@ -0,0 +1,15 @@
# POSIX makefile template for Nim
CFLAGS = -O2
LDFLAGS = -L/usr/lib
NC = nim
NCFLAGS = -d:release -d:lto -d:strip --opt:size
TARGET = toxio
DEPS =
$(TARGET): $(TARGET).nim
$(NC) $(NCFLAGS) -t:"$(CFLAGS)" -l:"$(LDFLAGS)" c $(TARGET).nim $(DEPS)
clean:
rm $(TARGET)
+61
View File
@@ -0,0 +1,61 @@
# toxio: turn (almost) any CLI program/script into a Tox bot
Toxio is a small Nim program that aims to do one thing: wrap any line-oriented CLI program or script to be able to communicate with it via the [Tox](https://tox.chat/) network. Toxio spawns any shell command of your choice as a subprocess and redirects your messages to its standard input and its output lines to the chat replies.
## Building
To build toxio, you obviously would need a working Nim compiler, as well as [toxcore](https://github.com/TokTok/c-toxcore) library and [nim-toxcore](https://git.sr.ht/~ehmry/nim-toxcore) package installed. The latter is available using Nimble package manager (`nimble install toxcore`). You will also need any POSIX-compatible `make` command, although nothing prevents you from compiling the `toxio.nim` file manually.
Once you have all dependencies sorted out, just run `make` in the project directory. If everything is successful, a `toxio` binary file should appear.
## Usage
The overall command syntax is like this: `toxio [bot_name] [save_file] [r|w] [cmd]`. Now, let's go through the stages to set up your bot.
### 1. Obtain a Tox save data file
First, you will need an existing toxcore-compatible save file. For safety reasons, toxio does not create any save files, you have to supply it externally. They may or may not already have friendlists, but if they do, beware that all the friends will have the access to the process running inside your bot. Thus, it's recommended to create a new profile save file (it must be unencrypted) in a client like Toxic and then use it solely for the purposes of this bot.
### 2. Add the bot users as friends
Now that you have your save file (e.g. `save.tox`), you should run toxio in the **write-enable** mode with the `w` command line parameter and a dummy command like `cat` (which will essentially make toxio function as an echo bot for you to test it):
```
toxio "My new bot" /path/to/save.tox w cat
```
In the write-enable mode, the bot accepts all friend requests and saves them to your save file. This mode must only be used for initial set up and testing phase, never for normal usage!
After running, the bot will print out its Tox ID that you can use to add as a friend to all your Tox identities you need to use the bot from. After you added this bot as a friend to every required ID, terminate it with Ctrl+C and make sure your save file is backed up in a safe place.
### 3. Run the bot normally
Now, you can run the bot normally (in the **read-only** mode) like this (where "My new bot" is just its displayed name and `"cmd"` is the command line you need to run):
```
toxio "My new bot" /path/to/save.tox r "cmd"
```
When toxio gets online, every connected friend will be able to communicate with the subprocess using the chat with this bot. The status message will display the command line of the process the bot is currently serving.
Keep in mind that you only start getting the program's output if you initially send a message. What to do with your messages is fully up to the program.
You can also pause the program's output. If you run, for instance, some monitoring service and want to temporarily stop receiving messages from the bot, just send `/toxiostop` to it. The output will resume automatically whenever you send any other message.
## Limitations
- Text messages only.
- Single subprocess per bot by design. This means that every connected friend will communicate with exactly the same subprocess and receive identical output from it.
- The program's output must end with newline characters.
- Every output line is sent as a separate message.
- The stdout and stderr streams are merged. This is hardcoded: find `poStdErrToStdOut` option in the `toxio.nim` file and remove it if you don't need this behavior.
- You need an external client to initially set up the bot's Tox ID and generate the save file.
- Only one DHT bootstrap node hardcoded for now.
- Only tested on Unix-like OSes, compatibility is not guaranteed for others.
## Credits
Created by Luxferre in 2024, released into public domain.
Made in Ukraine.
+100
View File
@@ -0,0 +1,100 @@
# 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)