This commit is contained in:
Wojciech Kwolek 2023-05-28 04:54:48 +02:00
commit 794dced81a
7 changed files with 403 additions and 0 deletions

163
.gitignore vendored Normal file
View file

@ -0,0 +1,163 @@
.env
*.sqlite3
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

13
Pipfile Normal file
View file

@ -0,0 +1,13 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
python-telegram-bot = "*"
peewee = "*"
[dev-packages]
[requires]
python_version = "3.11"

92
Pipfile.lock generated Normal file
View file

@ -0,0 +1,92 @@
{
"_meta": {
"hash": {
"sha256": "6ddb1ed12c4e907a637a2f354510df8c8c546693d1589b1e0ad7b5a482229af4"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.11"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"anyio": {
"hashes": [
"sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce",
"sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0"
],
"markers": "python_version >= '3.7'",
"version": "==3.7.0"
},
"certifi": {
"hashes": [
"sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
"sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
],
"markers": "python_version >= '3.6'",
"version": "==2023.5.7"
},
"h11": {
"hashes": [
"sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
"sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
],
"markers": "python_version >= '3.7'",
"version": "==0.14.0"
},
"httpcore": {
"hashes": [
"sha256:125f8375ab60036db632f34f4b627a9ad085048eef7cb7d2616fea0f739f98af",
"sha256:5581b9c12379c4288fe70f43c710d16060c10080617001e6b22a3b6dbcbefd36"
],
"markers": "python_version >= '3.7'",
"version": "==0.17.2"
},
"httpx": {
"hashes": [
"sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd",
"sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"
],
"markers": "python_version >= '3.7'",
"version": "==0.24.1"
},
"idna": {
"hashes": [
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
],
"markers": "python_version >= '3.5'",
"version": "==3.4"
},
"peewee": {
"hashes": [
"sha256:10769981198c7311f84a0ca8db892fa213303a8eb1305deb795a71e7bd606a91"
],
"index": "pypi",
"version": "==3.16.2"
},
"python-telegram-bot": {
"hashes": [
"sha256:1185edee387db7b08027e87b67fa9a3cc3263ae5ab5bb55513acd1bca5c3cf4b",
"sha256:73e46a534be9d1c790ce8b494765cca18a5c2f3f5b4932d83bcb06bb0051eb4a"
],
"index": "pypi",
"version": "==20.3"
},
"sniffio": {
"hashes": [
"sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101",
"sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"
],
"markers": "python_version >= '3.7'",
"version": "==1.3.0"
}
},
"develop": {}
}

0
topicbot/__init__.py Normal file
View file

108
topicbot/__main__.py Normal file
View file

@ -0,0 +1,108 @@
import logging
from telegram import Update
from telegram.constants import ChatType
from telegram.error import BadRequest
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters
from . import config
from .db import Channel
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!")
async def message(update: Update, context: ContextTypes.DEFAULT_TYPE):
if update.message is None:
return
if update.message.chat.type not in [ChatType.GROUP, ChatType.SUPERGROUP]:
await update.message.reply_text("can't set topics not in groups")
channel_id = str(update.message.chat.id)
channel = Channel.select().where(Channel.channel_id == channel_id).first()
if channel is None:
channel = Channel(channel_id=channel_id)
if update.message.text is None:
return
argv = (update.message.text.strip()+' ').split(' ', 1)
if len(argv) == 0 or argv[0] == '':
return
cmd, args = argv
cmd = cmd.split('@')[0].strip().lower()
args = args.strip()
async def edit_message():
await context.bot.edit_message_text(chat_id=channel_id, message_id=channel.topic_message_id, text=channel.topic)
async def send_message():
message = await context.bot.send_message(chat_id=channel_id, text=channel.topic)
channel.topic_message_id = message.message_id
async def pin_message(message_id):
try:
await context.bot.unpin_chat_message(chat_id=channel_id)
except BadRequest:
pass
await context.bot.pin_chat_message(chat_id=channel_id, message_id=message_id)
async def topic_update(new_topic):
channel.topic = new_topic
if channel.topic_message_id is None:
await send_message()
else:
try:
await edit_message()
except BadRequest:
await send_message()
await pin_message(channel.topic_message_id)
print("Changed topic to:", new_topic)
channel.save()
if cmd == '/topic':
if args == '':
if channel.topic is not None:
await update.message.reply_text(channel.topic)
return
await topic_update(args)
if cmd == '/append':
sep = ''
if len(args) > config.MAX_LENGTH:
await update.message.reply_text("this won't fit in the topic")
return
current_topic = []
if channel.topic is not None and len(channel.topic.strip()) != 0:
current_topic = channel.topic.split(sep)
current_topic.insert(0, args)
while len(sep.join(current_topic)) > config.MAX_LENGTH:
current_topic.pop()
await topic_update(sep.join(current_topic))
return
print(update, type(update))
application = ApplicationBuilder().token(config.TG_TOKEN).build()
start_handler = CommandHandler('start', start)
application.add_handler(start_handler)
message_handler = MessageHandler(filters.TEXT, message)
application.add_handler(message_handler)
application.run_polling()

5
topicbot/config.py Normal file
View file

@ -0,0 +1,5 @@
import os
DB_PATH=os.environ.get('DB_PATH', './database.sqlite3')
TG_TOKEN=os.environ['TG_TOKEN']
MAX_LENGTH=int(os.environ.get('MAX_LENGTH', 400))

22
topicbot/db.py Normal file
View file

@ -0,0 +1,22 @@
from peewee import *
from . import config
import os
db = SqliteDatabase(config.DB_PATH)
class BaseModel(Model):
class Meta:
database = db
class Channel(BaseModel):
channel_id = TextField()
topic = TextField()
topic_message_id = TextField()
db.connect()
db.create_tables([Channel])