Skip to content

Commit

Permalink
bug fixes, added Dockerfile
Browse files Browse the repository at this point in the history
  • Loading branch information
drui9 committed Jun 26, 2024
1 parent b7a6b01 commit 5667e8f
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 193 deletions.
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.12-alpine

RUN apk update && apk add gcc musl-dev python3-dev libffi-dev openssl-dev cargo make
RUN addgroup -S autogram && adduser -S autogram -G autogram

USER autogram
WORKDIR /home/autogram

COPY . .
RUN pip install .

CMD ["python3", "start.py"]
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 drui9
Copyright (c) 2022-2024 drui9

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
11 changes: 2 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
env := .venv
deps := requirements.txt

run:
@clear;./$(env)/bin/python start.py

build:
@./$(env)/bin/pip install build;./$(env)/bin/python -m build

clean:
@rm -rf dist build *.egg-info **/__pycache__/

stable: clean build
git push;git checkout releases;git merge main;git push;twine upload dist/*;git checkout main;

$(env): $(deps)
$(env):
python -m venv $@

install: $(env)
@./$(env)/bin/pip install -r $(deps)
@./$(env)/bin/pip install -e .

70 changes: 62 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@
<p>

## Installation ::
Autogram is a telegram bot-API wrapper written in python3, with a keen focus on remaining stupidly simple.

## QuickStart
`pip install autogram`
Autogram is a telegram-BOT API wrapper written in python3, with a keen focus on remaining stupidly simple.
- Copy either the Functional or OOP example below.
- Run the bot the first time to generate `project-name.json` config template
- Add your telegram bot token in the config, (from telegram:BotFather chat)
- Ready to run.
- Add your own logic and handler methods. `core.telegram.org` manual is your friend.

## `Why AutoGram?`
The name implies automated-telegram. I needed a framework that is easy and intuitive to work with.

## Usage
## Usage1: Functional
```python
from autogram import Autogram
from autogram.config import load_config, Start
from autogram.config import Start

#-- handle private dm
@Autogram.add('message')
Expand All @@ -31,15 +38,62 @@ def main(config):
bot.run() # every call fetches updates, and updates internal offset
#-- </start>
```
You should implement a loop over `bot.run()` to fetch updates inside your application. This makes the timing arbitrary, depending on where the program is expected to run. i.e offline gadgets that do not have consistent internet to use a webhook, or a server(with all the incredible webhook features using public IP/ngrok or custom domain name and a possibility of a HTTPS nginx endpoint).
## Usage2: OOP
```python
import time
from loguru import logger
from autogram import Autogram, Start

# --
class ExampleBot(Autogram):
def __init__(self, config):
super().__init__(config)

def run(self):
"""Custom implementation of bot.poll()"""
super().run() # initializes bot info, abstractmethod
for _ in range(10): # should be endless loop
offset = self.data('offset')
for rep in self.poll(offset=offset).json()['result']:
self.data('offset', rep.pop('update_id') + 1)
with self.register['lock']:
if handler := self.register['handlers'].get(list(rep.keys())[-1]):
handler(self, self, rep)
time.sleep(5)

@Autogram.add('message')
def message(self, bot: Autogram, update):
logger.debug(update['message']['text'])
chat_id = update['message']['chat']['id']
keyb = [[{'text': 'The armpit', 'callback_data': 'tickled'}]]
data = {
'reply_markup': bot.getInlineKeyboardMarkup(keyb)
}
bot.sendMessage(chat_id, 'Tickle me!', **data)

# --
@Autogram.add('callback_query')
def callback_query(self, bot: Autogram, update):
callback_id = update['callback_query']['id']
bot.answerCallbackQuery(callback_id, 'Ha-ha-ha')

#***************************** <start>
@Start()
def main(config):
bot = ExampleBot(config)
bot.run()
# ************ </start>
```
If you have a url-endpoint, call bot.setWebhook(url), then run some sort of webserver in bot.run.

## `Project TODOs`
- Tests.
- Add webhook support.
- Plans to cover the entire telegram API methods.
- Add webhook example.
- Extend coverage of the API.

### `footnotes`
- `Polling` is the default getUpdates() method.
- Invalid `TELEGRAM_TOKEN` return 404 through `bot.getMe()`
- Thread safety is not guaranteed for calls from multiple threads to: `bot.data, bot.settings`. Prefferably, handle the bot in a single thread, as handlers use these methods to persist data.
- Don't run multiple bots with the same `TOKEN` as this will cause update problems
- Sending unescaped special characters when using MarkdownV2 will return HTTP400
- Have `fun` with whatever you're building `;)`

23 changes: 10 additions & 13 deletions autogram/autogram.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import json
from threading import Event
from autogram.base import Bot
from abc import abstractmethod

# --
class Autogram(Bot):
#--
def __init__(self, config):
self.initialized = Event()
return super().__init__(config)

#--
@abstractmethod
def run(self):
if not self.initialized.is_set():
#-- load settings
Expand All @@ -18,14 +18,11 @@ def run(self):
except KeyError:
self.data('offset', 0)
#-- load self
if (bot := self.getMe()).ok:
for name, value in bot.json().items():
setattr(self, name, value)
self.initialized.set()
#--
offset = self.data('offset')
for rep in self.poll(offset=offset).json()['result']:
self.data('offset', rep.pop('update_id') + 1)
with self.register['lock']:
if handler := self.register['handlers'].get(list(rep.keys())[-1]):
handler(self, rep)
if (bot := self.getMe()).status_code != 200:
raise RuntimeError(bot.json())
#--
info = bot.json()['result']
for name, value in info.items():
setattr(self, name, value)
return self.initialized.set()

Loading

0 comments on commit 5667e8f

Please sign in to comment.