1
0
Fork
You've already forked 140characters
0
No description
  • Python 98.2%
  • Dockerfile 1%
  • Nix 0.8%
2026年01月23日 16:32:46 -05:00
onehundredfourty [states] search for state patterns using fullmatch 2025年12月07日 23:23:47 -05:00
.dockerignore [docker] update to use uv 2025年11月30日 14:14:02 -05:00
.gitignore [nix] add nix development environment 2026年01月23日 16:32:46 -05:00
.python-version [build] switch to uv 2025年11月30日 12:46:21 -05:00
Dockerfile [docker] change default command to point to /config 2025年12月02日 21:53:23 -05:00
init.py [init] allow custom bot names 2025年11月30日 13:08:54 -05:00
LICENSE LICENSE 2025年10月07日 20:19:16 -04:00
pyproject.toml [build] switch to uv 2025年11月30日 12:46:21 -05:00
README.md [readme] clean up readme 2025年10月11日 09:14:26 -04:00
shell.nix [nix] add nix development environment 2026年01月23日 16:32:46 -05:00
telegram_bot.py make fit for consumption 2025年10月07日 20:11:07 -04:00
uv.lock [build] switch to uv 2025年11月30日 12:46:21 -05:00

140characters

Have you ever wanted to post to mastodon using sms*? No? Now presenting my state-of-the-art** mastodon client: 140characters***!

*: telegram, though there is very only a loose dependency on the messenger itself

**: circa 2006

***: not actually restricted to 140 characters, i just thought it was cute that you used to be able to post to twitter using text messages.

running

To run the telegram frontend, you can use the Dockerfile. Ensure you have the app settings in a folder data/.

docker build -t 140ch_telegram_bot .
docker run -v data:/data 140ch_telegram_bot

settings.json

{
 "client_id": "",
 "client_secret": "",
 "access_token": "",
 "allowlist": [
 // list of strings of telegram user ids
 ],
 "base_url": "",
 "database": "",
 "telegram": {
 "token": ""
 }
}

background/technology

This is a python application formulated as a state machine. This means it can be hooked up to sms, telegram, or whatever your favorite messenger is with very little effort, as the input is a user state and the output is a user state and a text reply. User states are stored in sqlite, though other storage backends can be made to work though dependency injection.

example usage

Here is an example of using the bot on the console (a live example can be found here)

import logging
logging.basicConfig(level=logging.NOTSET)
logger = logging.getLogger(__name__)
import traceback
from onehundredfourty import states, Application, messaging, constants
from onehundredfourty.storage import SQLiteStorage
def handle_connection(identifier: str, msg: str, app: Application):
 logger.debug(f"Handling connection: identifier={identifier} msg={msg}")
 # if you choose to use the allowlist
 if not app.check_allowlist(identifier):
 return
 user = app.fetch_user(identifier)
 if user is None:
 user = app.register_user(identifier)
 # first interaction: send welcome message
 print(states.state_reply(app, user.state))
 try:
 state, reply = states.process_state(user.state)(app, user, msg)
 user.state = state
 if reply:
 print("\n".join([reply, states.state_reply(app, user.state)]).rstrip())
 else:
 print(states.state_reply(app, user.state))
 except Exception as e:
 logger.error(f"Error occurred while processing user state! {e}")
 logger.error(traceback.format_exc())
 user.state, _ = states.EXCEPTION_LANDING
 app.save_user(user)
def main():
 storage = SQLiteStorage("path_to_sqlite.db")
 storage.ensure_storage()
 app = Application(
 "mastodon.instance",
 "mastodon client id",
 "mastodon client secret",
 storage,
 set(), # allowlist
 )
 while True:
 msg = input("> ")
 handle_connection("localhost", msg, app)
if __name__ == "__main__":
 main()