generated from Beesquit/telegram-bot-template
Implemented last article command
This commit is contained in:
@@ -1,15 +1,12 @@
|
||||
Project template for simple telegram bots
|
||||
### Telegram bot that uses [habr article api](https://git.frik.su/n0one/habr-article-API) for sending articles to user
|
||||
|
||||
Features:
|
||||
- Base commands with FSM (no redis)
|
||||
- JSON data storage
|
||||
- Async worker function
|
||||
- Cute project structure
|
||||
Commands:
|
||||
- /start
|
||||
- /help
|
||||
- /articles
|
||||
- url
|
||||
|
||||
.env example:
|
||||
```.env
|
||||
botToken="token:from-botfather"
|
||||
whitelist=["userid"]
|
||||
dataPath="data/"
|
||||
jsonFilename="data.json"
|
||||
```
|
||||
To do:
|
||||
- /rates
|
||||
- /rate
|
||||
- /remove-rate
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import asyncio
|
||||
|
||||
from app.jsonData import start as start_json
|
||||
from app.telegram import start_bot
|
||||
|
||||
|
||||
def createApp():
|
||||
start_json()
|
||||
asyncio.run(start_bot())
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from .utils import getArticles
|
||||
|
||||
__all__ = ["getArticles"]
|
||||
@@ -0,0 +1,22 @@
|
||||
import base64
|
||||
import json
|
||||
|
||||
import httpx
|
||||
|
||||
habrApiUrl="https://habr.frik.su/api/".removesuffix("/")
|
||||
|
||||
|
||||
async def getArticles(amount: int) -> list[tuple[str, str]]:
|
||||
result = []
|
||||
async with httpx.AsyncClient() as client:
|
||||
payload = {
|
||||
"amount": amount
|
||||
}
|
||||
response = await client.post(habrApiUrl + "/articles/get/md", json=payload)
|
||||
|
||||
responseJson = json.loads(response.content)
|
||||
|
||||
for key in responseJson:
|
||||
decoded = base64.b64decode(responseJson[key].encode('ascii')).decode('utf-8')
|
||||
result.append((key, decoded))
|
||||
return result
|
||||
@@ -1,8 +0,0 @@
|
||||
import asyncio
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
||||
async def internal():
|
||||
logger.info("Internal")
|
||||
await asyncio.sleep(4)
|
||||
@@ -1,11 +0,0 @@
|
||||
from .connect import load, start
|
||||
from .utils import add, check, removeById, removeByParam
|
||||
|
||||
__all__ = [
|
||||
"load",
|
||||
"start",
|
||||
"add",
|
||||
"check",
|
||||
"removeById",
|
||||
"removeByParam",
|
||||
]
|
||||
@@ -1,47 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from app.settings import settings
|
||||
|
||||
dataPath = settings.dataPath.removesuffix("/")
|
||||
jsonRawFilename = settings.jsonFilename.removesuffix(".json")
|
||||
backupPath = dataPath + "/backup"
|
||||
filePath = dataPath + "/" + jsonRawFilename + ".json"
|
||||
|
||||
|
||||
def start():
|
||||
# Inits JSON file
|
||||
try:
|
||||
if not(os.path.exists(dataPath)):
|
||||
os.mkdir(dataPath)
|
||||
if not(os.path.exists(backupPath)):
|
||||
os.mkdir(backupPath)
|
||||
|
||||
if os.path.exists(filePath):
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
backupFilePath = backupPath + "/" + jsonRawFilename + f"_backup_{timestamp}.json"
|
||||
shutil.copy(filePath, backupFilePath)
|
||||
logger.info(f"JSON backup was created: {backupFilePath}")
|
||||
|
||||
with open(filePath, "w") as f:
|
||||
json.dump({}, f, ensure_ascii = False, indent=4)
|
||||
logger.success(f"Successfully initialized JSON with path: {filePath}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize JSON: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def load():
|
||||
# Returnes the contents of the JSON file as a dictionary
|
||||
data = {}
|
||||
try:
|
||||
with open(filePath, "r") as f:
|
||||
data = json.load(f)
|
||||
except json.decoder.JSONDecodeError:
|
||||
logger.warning("JSON file is empty!")
|
||||
return data
|
||||
@@ -1,69 +0,0 @@
|
||||
import json
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from app.jsonData.connect import load
|
||||
from app.settings import settings
|
||||
|
||||
|
||||
def getParamId(param):
|
||||
# Returnes -1 if param not found and id if found
|
||||
currentData = load()
|
||||
|
||||
id = -1
|
||||
for (i, j) in [(i[0], i[1]) for i in currentData]:
|
||||
if j == param:
|
||||
id = i
|
||||
break
|
||||
return id
|
||||
|
||||
|
||||
def check(param):
|
||||
# Returnes 1 if param exists and 0 if not
|
||||
currentData = load()
|
||||
|
||||
if param in [i[1] for i in currentData]:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def removeById(id: int):
|
||||
# Returnes 0 if deleted successfully and -1 if not
|
||||
currentData = load()
|
||||
|
||||
if id in currentData:
|
||||
del currentData[id]
|
||||
with open(settings.jsonFilename, 'w', encoding = 'utf-8') as f:
|
||||
json.dump(currentData, f, ensure_ascii = False, indent = 4)
|
||||
logger.info(f'Id {id} was deleted successfully!')
|
||||
return 0
|
||||
else:
|
||||
logger.info(f'Id {id} was not found in the data file when trying to delete it.')
|
||||
return -1
|
||||
|
||||
def removeByParam(param):
|
||||
# Returnes 0 if deleted successfully and -1 if not
|
||||
id = getParamId(param)
|
||||
|
||||
if id != -1:
|
||||
return removeById(id)
|
||||
else:
|
||||
logger.info(f'Param {param} was not found in the data file when trying to delete it.')
|
||||
return -1
|
||||
|
||||
|
||||
def add(param):
|
||||
# Saves or updates data in JSON
|
||||
currentData = load()
|
||||
if currentData == {}:
|
||||
id = 0
|
||||
else:
|
||||
id = currentData[-1][0] + 1
|
||||
newData = {id: param}
|
||||
|
||||
with open(settings.jsonFilename, 'w', encoding = 'utf-8') as f:
|
||||
currentData.update(newData)
|
||||
json.dump(currentData, f, ensure_ascii = False, indent = 4)
|
||||
logger.info(f"Param {param} was added with id {id}")
|
||||
return id
|
||||
@@ -9,24 +9,18 @@ unexpectedError = "Unexpected error"
|
||||
|
||||
# Commands
|
||||
|
||||
startCommand = "Hi"
|
||||
startCommand = "Hi. I'm open source bot for reading habr articles from telegram. Here is your chatid btw: "
|
||||
|
||||
helpCommand = "This is help"
|
||||
helpCommand = "Commands are obvious no help needed, hehe"
|
||||
|
||||
|
||||
# Route status
|
||||
|
||||
askParam = "Enter param:"
|
||||
|
||||
successAdd = "Added successfully"
|
||||
|
||||
successRemove = "Removed successfully"
|
||||
|
||||
alreadyExists = "Key already exists"
|
||||
askParam = "Enter url:"
|
||||
|
||||
|
||||
# Data status
|
||||
|
||||
noData = "No data"
|
||||
|
||||
foundData = "Founde data:"
|
||||
foundData = "Found data:"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import asyncio
|
||||
|
||||
from aiogram import Bot, Dispatcher, F, Router
|
||||
from aiogram.client.default import DefaultBotProperties
|
||||
from aiogram.enums import ParseMode
|
||||
@@ -10,8 +8,7 @@ from aiogram.fsm.storage.memory import MemoryStorage
|
||||
from aiogram.types import BotCommand, BotCommandScopeDefault, Message
|
||||
from loguru import logger
|
||||
|
||||
from app import jsonData
|
||||
from app.internal.internal import internal
|
||||
from app import httpxHandler
|
||||
from app.settings import settings
|
||||
from app.telegram import strings
|
||||
|
||||
@@ -21,14 +18,14 @@ async def setCommands():
|
||||
BotCommand(command='start', description='Start'),
|
||||
BotCommand(command='help', description='Help'),
|
||||
BotCommand(command='info', description='Info'),
|
||||
BotCommand(command='add', description='Add'),
|
||||
BotCommand(command='last', description='last'),
|
||||
BotCommand(command='remove', description='Remove')
|
||||
]
|
||||
await bot.set_my_commands(commands, BotCommandScopeDefault())
|
||||
|
||||
|
||||
class addForm(StatesGroup):
|
||||
param = State()
|
||||
class ArticleForm(StatesGroup):
|
||||
url = State()
|
||||
|
||||
class removeForm(StatesGroup):
|
||||
param = State()
|
||||
@@ -46,7 +43,7 @@ removeRouter = Router()
|
||||
|
||||
@dp.message(Command("start"))
|
||||
async def commandStart(message: Message) -> None:
|
||||
await message.answer(strings.startCommand)
|
||||
await message.answer(strings.startCommand + str(message.chat.id))
|
||||
|
||||
@dp.message(Command("help"), F.chat.id.in_(settings.whitelist))
|
||||
async def commandHelp(message: Message) -> None:
|
||||
@@ -54,52 +51,33 @@ async def commandHelp(message: Message) -> None:
|
||||
|
||||
@dp.message(Command("info"), F.chat.id.in_(settings.whitelist))
|
||||
async def commandInfo(message: Message) -> None:
|
||||
data = jsonData.load()
|
||||
msgText = ''
|
||||
if data == {}:
|
||||
msgText = strings.noData
|
||||
else:
|
||||
msgText = strings.foundData
|
||||
for i in data:
|
||||
msgText += (f"<b>{str(i[0])}</b>: </b>{str(i[1])}</b>\n")
|
||||
msgText = ""
|
||||
await message.answer(msgText)
|
||||
|
||||
|
||||
@strategyRouter.message(Command("add"), F.chat.id.in_(settings.whitelist))
|
||||
async def commandAdd(message: Message, state: FSMContext):
|
||||
await message.answer(strings.askParam)
|
||||
await state.set_state(addForm.param)
|
||||
@dp.message(Command("last"), F.chat.id.in_(settings.whitelist))
|
||||
async def commandLast(message: Message):
|
||||
articles = await httpxHandler.getArticles(1)
|
||||
if articles == []:
|
||||
await message.answer(strings.unexpectedError)
|
||||
else:
|
||||
for (url, content) in articles:
|
||||
print(url, content)
|
||||
await message.answer("Url: " + url + "\n" + content)
|
||||
|
||||
@strategyRouter.message(F.text, addForm.param)
|
||||
|
||||
@strategyRouter.message(Command("get"), F.chat.id.in_(settings.whitelist))
|
||||
async def commandGet(message: Message, state: FSMContext):
|
||||
await message.answer(strings.askParam)
|
||||
await state.set_state(ArticleForm.url)
|
||||
|
||||
@strategyRouter.message(F.text, ArticleForm.url)
|
||||
async def captureStartPair(message: Message, state: FSMContext):
|
||||
await state.update_data(pair=message.text)
|
||||
data = await state.get_data()
|
||||
param = data.get("param")
|
||||
|
||||
t = jsonData.add(param)
|
||||
msgText = strings.successAdd + str(t)
|
||||
await asyncio.to_thread(internal)
|
||||
|
||||
await message.answer(msgText)
|
||||
await state.clear()
|
||||
|
||||
|
||||
@removeRouter.message(Command("remove"), F.chat.id.in_(settings.whitelist))
|
||||
async def commandRemove(message: Message, state: FSMContext):
|
||||
await message.answer(strings.askParam)
|
||||
await state.set_state(removeForm.param)
|
||||
|
||||
@removeRouter.message(F.text, removeForm.param)
|
||||
async def captureRemoveParam(message: Message, state: FSMContext):
|
||||
await state.update_data(pair=message.text)
|
||||
data = await state.get_data()
|
||||
param = data.get("param")
|
||||
|
||||
t = jsonData.removeByParam(param)
|
||||
if t == 0:
|
||||
msgText = strings.successRemove
|
||||
else:
|
||||
msgText = strings.noData
|
||||
url = data.get("url")
|
||||
msgText = ""
|
||||
await httpxHandler.getArticles(1)
|
||||
|
||||
await message.answer(msgText)
|
||||
await state.clear()
|
||||
|
||||
Reference in New Issue
Block a user