58 Commits

Author SHA1 Message Date
39fa7b9632 Update dependency libbot to v1.4 2023-06-26 14:16:19 +03:00
3170274a13 Merge pull request 'Update dependency libbot to v1' (#18) from renovate/libbot-1.x into master
Reviewed-on: #18
2023-06-20 14:13:05 +03:00
32d9f76e38 Update dependency libbot to v1 2023-06-20 14:02:10 +03:00
0b82d39aab Merge pull request 'Update dependency puppeteer to ~20.7.0' (#16) from renovate/puppeteer-20.x into master
Reviewed-on: #16
2023-06-14 11:48:42 +03:00
cf522ab254 Merge pull request 'Update dependency libbot to v0.8' (#15) from renovate/libbot-0.x into master
Reviewed-on: #15
2023-06-14 11:48:20 +03:00
7be86f04c2 Update dependency puppeteer to ~20.7.0 2023-06-14 11:40:54 +03:00
4bc5ffb867 Update dependency libbot to v0.8 2023-06-14 11:40:51 +03:00
1be04dbea0 Merge pull request 'Update dependency ujson to v5.8.0' (#14) from renovate/ujson-5.x into master
Reviewed-on: #14
2023-06-11 12:29:24 +03:00
b4b102421f Update dependency ujson to v5.8.0 2023-06-11 12:17:53 +03:00
852c7d962a Merge pull request 'Update dependency libbot to v0.7' (#12) from renovate/libbot-0.x into master
Reviewed-on: #12
2023-06-04 12:46:09 +03:00
d7c393b5cd Update dependency libbot to v0.7 2023-06-03 20:50:31 +03:00
c2828f1baf Merge pull request 'Update dependency puppeteer to ~20.5.0' (#13) from renovate/puppeteer-20.x into master
Reviewed-on: #13
2023-06-03 20:47:26 +03:00
de3ecc22be Update dependency puppeteer to ~20.5.0 2023-05-31 16:35:01 +03:00
d98afc53ca Merge pull request 'Update dependency puppeteer to ~20.4.0' (#11) from renovate/puppeteer-20.x into master
Reviewed-on: #11
2023-05-25 11:45:57 +03:00
997da7bd2f Update dependency puppeteer to ~20.4.0 2023-05-25 08:20:21 +03:00
4a90544b52 Added node_modules to ignore 2023-05-22 20:52:44 +02:00
45d8c830d6 Merge pull request 'Update dependency puppeteer to ~20.3.0' (#10) from renovate/puppeteer-20.x into master
Reviewed-on: #10
2023-05-22 14:54:21 +03:00
8ca5916be5 Update dependency puppeteer to ~20.3.0 2023-05-22 11:28:51 +03:00
185235a336 Moved command registration to the events on start 2023-05-16 20:53:59 +02:00
ccb9e79cd7 Fixed naming error 2023-05-16 20:51:48 +02:00
69d63ca1ce Large refactor 2023-05-16 20:50:18 +02:00
fe5f531c8d Updated ignore 2023-05-16 20:49:53 +02:00
671040bd3b Merge pull request 'Update dependency puppeteer to ~20.2.0' (#9) from renovate/puppeteer-20.x into master
Reviewed-on: #9
2023-05-11 23:05:53 +03:00
0037b22937 Update dependency puppeteer to ~20.2.0 2023-05-11 23:04:22 +03:00
767ae6b20a Merge pull request 'Update dependency puppeteer to v20' (#8) from renovate/puppeteer-20.x into master
Reviewed-on: #8
2023-05-08 11:50:46 +03:00
65dc06617d Merge pull request 'Update dependency pyrogram to v2.0.106' (#7) from renovate/pyrogram-2.x into master
Reviewed-on: #7
2023-05-08 11:50:33 +03:00
e38ab26c93 Update dependency puppeteer to v20 2023-05-08 11:46:30 +03:00
73a140c0bb Update dependency pyrogram to v2.0.106 2023-05-08 11:46:26 +03:00
c2142416a8 Merge pull request 'Update dependency puppeteer to ~19.11.0' (#6) from renovate/puppeteer-19.x into master
Reviewed-on: #6
2023-04-24 14:45:27 +03:00
2b2de71719 Update dependency puppeteer to ~19.11.0 2023-04-24 14:37:01 +03:00
585865a61f Merge pull request 'Update dependency pyrogram to v2.0.104' (#5) from renovate/pyrogram-2.x into master
Reviewed-on: #5
2023-04-23 17:32:24 +03:00
435a2cc8c7 Update dependency pyrogram to v2.0.104 2023-04-23 12:07:18 +03:00
e1bed772ef Made a few dependencies strict 2023-04-23 10:31:35 +02:00
5a99610aa8 Merge pull request 'Update dependency puppeteer to ~19.10.0' (#4) from renovate/puppeteer-19.x into master
Reviewed-on: #4
2023-04-21 09:55:03 +03:00
2dc3cefda4 Update dependency puppeteer to ~19.10.0 2023-04-20 19:16:26 +03:00
eb913eb42a Merge pull request 'Update dependency puppeteer to ~19.9.0' (#2) from renovate/puppeteer-19.x into master
Reviewed-on: #2
2023-04-20 14:29:12 +03:00
8ac38405cf Update dependency puppeteer to ~19.9.0 2023-04-20 14:27:58 +03:00
36cbb1a41f Renamed Renovate config 2023-04-20 13:27:14 +02:00
d74826f0b4 Merge pull request 'Configure Renovate' (#1) from renovate/configure into master
Reviewed-on: #1
2023-04-20 14:24:40 +03:00
b23f0f2436 Add renovate.json 2023-04-20 14:13:38 +03:00
4f423257f3 Bump pyrogram and beautifulsoup4 2023-04-19 14:31:21 +02:00
9fb509fc2b Changed conversation handler to convopyro 2023-03-18 01:15:21 +01:00
82a659cce2 Formatted README 2023-03-18 00:52:36 +01:00
c3dd6f61d6 Formatted everything with black 2023-03-18 00:52:15 +01:00
dcf82ab6f2 Bump BS4 and Pyrogram versions 2023-03-18 00:47:45 +01:00
Profitroll
1dd8b13297 Improved logging 2023-01-14 13:25:18 +01:00
Profitroll
200f25e130 Updated dependencies 2023-01-14 13:25:09 +01:00
Profitroll
a09c3fb0d4 New error message added 2023-01-14 13:25:02 +01:00
Profitroll
affb54155c User agent randomized 2023-01-14 13:24:44 +01:00
Profitroll
b97b10975d Removed unused imports 2022-12-30 22:15:24 +01:00
Profitroll
27e204d3cc Updated requirements 2022-12-30 22:13:18 +01:00
Profitroll
4fd4f0a6a4 Improved imports 2022-12-30 22:13:10 +01:00
Profitroll
19b83c0631 Using .run instead of .system now 2022-12-30 21:03:04 +01:00
fa8bdc0e1f ignoring .vscode now 2022-12-15 12:05:22 +01:00
b9a7d85674 Improved requirements 2022-12-15 12:05:14 +01:00
fe1c6984b2 Option to use compiled page saver 2022-09-08 13:04:37 +02:00
e7ef1d4613 Added encoding header 2022-09-08 12:54:43 +02:00
85a756dcab Added license badge 2022-09-08 12:54:13 +02:00
20 changed files with 511 additions and 357 deletions

12
.gitignore vendored
View File

@@ -152,3 +152,15 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Project specific
.vscode
config.json
*.session
*.session-journal
venv
venv_linux
venv_windows
PageSaver/node_modules

6
.renovaterc Normal file
View File

@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}

View File

@@ -1,5 +1,6 @@
{
"dependencies": {
"puppeteer": "^14.4.0"
"puppeteer": "~20.7.0",
"user-agents": "~1.0.1260"
}
}

View File

@@ -1,12 +1,14 @@
// npm install https://github.com/GoogleChrome/puppeteer/
const puppeteer = require('puppeteer');
const userAgent = require('user-agents');
(async () => {
const url = process.argv[2];
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setUserAgent(userAgent.random().toString());
await page.goto(url, {waitUntil: 'load'});

View File

@@ -1,12 +1,17 @@
# BWTAqua
[![License: GPL v3](https://img.shields.io/badge/License-GPL_v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.html)
Simple yet helpful bot to check BWT Aqua's card balance
## Requirements
* nodejs & npm
* python3
* git
## Installation
1. Download package
1. `git clone https://git.end-play.xyz/profitroll/BWTAqua.git`
2. `cd BWTAqua`
@@ -15,6 +20,7 @@ Simple yet helpful bot to check BWT Aqua's card balance
3. Install PageSaver:
1. `cd PageSaver`
2. `npm install`
3. `chmod +x pageSaver` (If you want to use compiled page saver)
4. Configure the bot:
1. `cd ..`
2. `nano config.json` (You can use any other text editor actually, for example `vim`)
@@ -22,5 +28,6 @@ Simple yet helpful bot to check BWT Aqua's card balance
* `python3 bwtbot.py`
## Configuration
You can edit with vim, nano, on Windows it's Notepad or Notepad++. Whatever.
You can edit with vim, nano, whatever.
If you don't know where to find bot_token and your id - here you can find some hints: [get bot token](https://www.siteguarding.com/en/how-to-get-telegram-bot-api-token), [get your id](https://www.alphr.com/telegram-find-user-id/), [get api_hash and api_id](https://core.telegram.org/api/obtaining_api_id).

188
bwtbot.py
View File

@@ -1,173 +1,37 @@
#-*- coding: utf-8 -*-
import logging
from os import getpid
import traceback
from pyrogram import Client, filters, idle
from pyrogram.types import ForceReply, BotCommand, BotCommandScopeChat
from pyrogram.enums.chat_action import ChatAction
from functions import *
from modules.colors import *
from modules.bwt import *
import subprocess
import os
from convopyro import Conversation
config = jsonLoad("config.json")
from modules.app import PyroClient
owner_id = config["owner_id"]
logging.basicConfig(
level=logging.INFO,
format="%(name)s.%(funcName)s | %(levelname)s | %(message)s",
datefmt="[%X]",
)
app = Client(config["bot_name"], api_id=config["api_id"], api_hash=config["api_hash"], bot_token=config["bot_token"])
logger = logging.getLogger(__name__)
@app.on_message(~ filters.scheduled & filters.command(["setcard", "задать карту"], prefixes=["/", ""]))
async def setcard(_, msg):
if userGet(msg.from_user.id, "context") is None:
userSet(msg.from_user.id, "context", "set")
await msg.reply_text(string("send_number"), reply_markup=ForceReply(placeholder=string("enter_number")))
else:
await msg.reply_text(string("cancel_first"))
@app.on_message(~ filters.scheduled & filters.command(["cancel", "відміна"], prefixes=["/", ""]))
async def cancel(_, msg):
if userGet(msg.from_user.id, "context") is not None:
userReset(msg.from_user.id, "context")
await msg.reply_text(string("cancel"))
else:
await msg.reply_text(string("cancel_none"))
@app.on_message(~ filters.scheduled & filters.command(["resetcard", "забути картку"], prefixes=["/", ""]))
async def resetcard(_, msg):
if userGet(msg.from_user.id, "context") is None:
if "card" in jsonLoad("data/database.json")[str(msg.from_user.id)]:
userReset(msg.from_user.id, "card")
await msg.reply_text(string("card_unlinked"))
appendLog(f"User {str(msg.from_user.id)} reseted his card")
else:
await msg.reply_text(string("card_not_linked").format(string("get_number")))
appendLog(f"User {str(msg.from_user.id)} tried to reset non-existent card")
else:
await msg.reply_text(string("cancel_first"))
@app.on_message(~ filters.scheduled & filters.command(["balance", "баланс"], prefixes=["/", ""]))
async def balance(_, msg):
if userGet(msg.from_user.id, "context") is None:
try:
if "card" in jsonLoad("data/database.json")[str(msg.from_user.id)]:
await app.send_chat_action(chat_id=msg.chat.id, action=ChatAction.TYPING)
water_left = await getWaterLeft(userGet(msg.from_user.id, "card"), msg.from_user.id, app)
if water_left == "":
raise EmptyCardException("Card information is empty")
elif water_left == "Failure":
await msg.reply_text(string("error_occured").format(string("get_number")))
appendLog(f"User {str(msg.from_user.id)} could not get left water amount")
else:
await msg.reply_text(string("card_balance").format(water_left))
appendLog(f"User {str(msg.from_user.id)} has {water_left} liters remaining")
else:
await msg.reply_text(string("card_not_linked").format(string("get_number")))
appendLog(f"User {str(msg.from_user.id)} tried to get balance without card set")
except Exception as exp:
await msg.reply_text(string("error_occured").format(string("get_number")))
await app.send_message(owner_id, f"Error occured by {str(msg.from_user.id)}:\nException: `{exp}`\nTraceback: `{traceback.format_exc()}`")
appendLog(f"User {str(msg.from_user.id)} could not get left water amount")
else:
await msg.reply_text(string("cancel_first"))
import uvloop
uvloop.install()
except ImportError:
pass
@app.on_message(~ filters.scheduled & filters.command(["topup", "refill", "поповнити"], prefixes=["/", ""]))
async def topup_cmd(_, msg):
if userGet(msg.from_user.id, "context") is None:
def main():
client = PyroClient()
Conversation(client)
try:
if "card" in jsonLoad("data/database.json")[str(msg.from_user.id)]:
await app.send_chat_action(chat_id=msg.chat.id, action=ChatAction.TYPING)
await msg.reply_text(string("top_up").format(str(userGet(msg.from_user.id, "card"))))
appendLog(f"User {str(msg.from_user.id)} requested top up")
else:
await msg.reply_text(string("card_not_linked").format(string("get_number")))
appendLog(f"User {str(msg.from_user.id)} tried to request top up without card set")
except Exception as exp:
await msg.reply_text(exp)
else:
await msg.reply_text(string("cancel_first"))
client.run()
except KeyboardInterrupt:
logger.warning(f"Forcefully shutting down with PID {getpid()}...")
finally:
exit()
@app.on_message(~ filters.scheduled & filters.command(["start", "help", "допомога"], prefixes=["/", ""]))
async def help(_, msg):
if userGet(msg.from_user.id, "context") is None:
await msg.reply_text(string("welcome").format(string("get_number")))
if msg.from_user.language_code in jsonLoad("strings.json"):
userSet(msg.from_user.id, "locale", msg.from_user.language_code)
else:
userSet(msg.from_user.id, "locale", "en")
else:
await msg.reply_text(string("cancel_first"))
pid = os.getpid()
@app.on_message(~ filters.scheduled & filters.command(["kill", "die", "shutdown"], prefixes="/"))
async def kill(_, msg):
if msg.from_user.id == owner_id:
await msg.reply_text(f"Shutting down bot with pid **{pid}**")
os.system(f"kill -9 {pid}")
@app.on_message(~ filters.scheduled)
async def any_message_handler(app, msg):
if userGet(msg.from_user.id, "context") == "set":
userSet(msg.from_user.id, "card", msg.text)
userReset(msg.from_user.id, "context")
appendLog(f"User {str(msg.from_user.id)} set card id to {msg.text}")
await msg.reply_text(string("card_linked").format(msg.text))
print(f'{nowtime()} {WHITE}Starting with PID {YELLOW}{pid}{RESET}')
app.start()
app.send_message(owner_id, f"Starting bot with pid **{pid}**")
app.set_bot_commands([
BotCommand("help", "Меню допомоги"),
BotCommand("balance", "Баланс картки"),
BotCommand("topup", "Поповнити картку"),
BotCommand("setcard", "Прив'язати картку"),
BotCommand("resetcard", "Відв'язати картку"),
BotCommand("cancel", "Відмінити операцію"),
],
language_code="uk")
app.set_bot_commands([
BotCommand("help", "Меню допомоги"),
BotCommand("balance", "Баланс картки"),
BotCommand("topup", "Поповнити картку"),
BotCommand("setcard", "Прив'язати картку"),
BotCommand("resetcard", "Відв'язати картку"),
BotCommand("cancel", "Відмінити операцію"),
],
language_code="ru")
app.set_bot_commands([
BotCommand("help", "Help menu"),
BotCommand("balance", "Card's balance"),
BotCommand("topup", "Refill card"),
BotCommand("setcard", "Link card"),
BotCommand("resetcard", "Unlink card"),
BotCommand("cancel", "Cancel operation"),
])
app.set_bot_commands([
BotCommand("help", "Help menu"),
BotCommand("balance", "Card's balance"),
BotCommand("topup", "Refill card"),
BotCommand("setcard", "Link card"),
BotCommand("resetcard", "Unlink card"),
BotCommand("shutdown", "Turn off the bot"),
BotCommand("cancel", "Cancel operation"),
],
scope=BotCommandScopeChat(chat_id=owner_id))
idle()
app.send_message(owner_id, f"Shutting down bot with pid **{pid}**")
print(f'\n{nowtime()} {WHITE}Shutting down with PID {YELLOW}{pid}{RESET}')
subprocess.call(f'kill -9 {pid}', shell=True)
if __name__ == "__main__":
main()

View File

@@ -1,8 +1,10 @@
{
"owner_id": 0,
"log_size": 1024,
"bot": {
"api_id": 0,
"api_hash": "",
"bot_token": "",
"bot_name": ""
"workers": 1
},
"use_compiled_page_saver": false
}

View File

@@ -1,121 +0,0 @@
import json
import os
import shutil
import gzip
import time
from modules.colors import *
from datetime import datetime
from pathlib import Path
path = Path(__file__).resolve().parent
days_path = str(path)+"/assets/days/"
users_path = str(path)+"/users/"
logs_folder = str(path)+"/logs/"
def jsonSave(filename, value):
with open(filename, 'w', encoding="utf-8") as f:
json.dump(value, f, indent=4, ensure_ascii=False)
f.close()
def jsonLoad(filename):
with open(filename, 'r', encoding="utf-8") as f:
value = json.load(f)
f.close()
return value
config = jsonLoad(f"{path}/config.json")
log_size = config["log_size"]
owner_id = config["owner_id"]
def nowtime():
return f'{BBLACK}[{CYAN}{datetime.now().strftime("%H:%M:%S")}{BBLACK}]{RESET}'
def checkSize():
global logs_folder, log_size
i = 0
while i < 2:
try:
log = os.stat(logs_folder + 'latest.log')
if (log.st_size / 1024) > log_size:
with open(logs_folder + 'latest.log', 'rb') as f_in:
with gzip.open(f'{logs_folder}{datetime.now().strftime("%d.%m.%Y_%H:%M:%S")}.zip', 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
open(logs_folder + 'latest.log', 'w').close()
i = 2
except FileNotFoundError:
try:
log = open(logs_folder + 'latest.log', 'a')
open(logs_folder + 'latest.log', 'a').close()
except:
try:
os.mkdir(logs_folder)
log = open(logs_folder + 'latest.log', 'a')
open(logs_folder + 'latest.log', 'a').close()
except:
pass
i += 1
def appendLog(message):
global logs_folder
checkSize()
try:
log = open(logs_folder + 'latest.log', 'a')
open(logs_folder + 'latest.log', 'a').close()
except:
try:
os.mkdir(logs_folder)
log = open(logs_folder + 'latest.log', 'a')
open(logs_folder + 'latest.log', 'a').close()
except:
time.sleep(2)
print('Log file could not be created')
return
log.write(f'[{datetime.now().strftime("%H:%M:%S | %d.%m.%Y")}] {message}\n')
log.close()
def string(key: str, *args: str, userlocale="uk"):
locales = jsonLoad("strings.json")
strings = locales[userlocale]
string = strings
for dict_key in args:
string = string[dict_key]
return string[key]
def userSet(userid, key: str, value):
database = jsonLoad("data/database.json")
if str(userid) not in database:
database[str(userid)] = {}
database[str(userid)][key] = value
jsonSave("data/database.json", database)
def userReset(userid, key: str):
database = jsonLoad("data/database.json")
del database[str(userid)][key]
jsonSave("data/database.json", database)
def userGet(userid, key: str):
try:
return jsonLoad("data/database.json")[str(userid)][key]
except KeyError:
return None
except FileNotFoundError:
return None

104
modules/app.py Normal file
View File

@@ -0,0 +1,104 @@
import logging
from os import getpid
from time import time
import pyrogram
from libbot import config_get
from pyrogram.client import Client
from pyrogram.errors import BadRequest
from pyrogram.raw.all import layer
from pyrogram.types import BotCommand, BotCommandScopeChat
from ujson import loads
logger = logging.getLogger(__name__)
class PyroClient(Client):
def __init__(self):
with open("config.json", "r", encoding="utf-8") as f:
config = loads(f.read())
super().__init__(
name="bwtbot",
api_id=config["bot"]["api_id"],
api_hash=config["bot"]["api_hash"],
bot_token=config["bot"]["bot_token"],
workers=config["bot"]["workers"],
plugins=dict(root="plugins", exclude=config["disabled_plugins"]),
sleep_threshold=120,
)
async def start(self):
await super().start()
self.start_time = time()
logger.info(
"Bot is running with Pyrogram v%s (Layer %s) and has started as @%s on PID %s.",
pyrogram.__version__,
layer,
self.me.username,
getpid(),
)
try:
await self.send_message(
chat_id=await config_get("chat_id", "reports"),
text=f"Bot started PID `{getpid()}`",
)
except BadRequest:
logger.warning("Unable to send message to report chat.")
await self.set_bot_commands(
[
BotCommand("help", "Меню допомоги"),
BotCommand("balance", "Баланс картки"),
BotCommand("topup", "Поповнити картку"),
BotCommand("setcard", "Прив'язати картку"),
BotCommand("resetcard", "Відв'язати картку"),
],
language_code="uk",
)
await self.set_bot_commands(
[
BotCommand("help", "Меню допомоги"),
BotCommand("balance", "Баланс картки"),
BotCommand("topup", "Поповнити картку"),
BotCommand("setcard", "Прив'язати картку"),
BotCommand("resetcard", "Відв'язати картку"),
],
language_code="ru",
)
await self.set_bot_commands(
[
BotCommand("help", "Help menu"),
BotCommand("balance", "Card's balance"),
BotCommand("topup", "Refill card"),
BotCommand("setcard", "Link card"),
BotCommand("resetcard", "Unlink card"),
]
)
await self.set_bot_commands(
[
BotCommand("help", "Help menu"),
BotCommand("balance", "Card's balance"),
BotCommand("topup", "Refill card"),
BotCommand("setcard", "Link card"),
BotCommand("resetcard", "Unlink card"),
BotCommand("shutdown", "Turn off the bot"),
],
scope=BotCommandScopeChat(chat_id=await config_get("owner_id")),
)
async def stop(self):
try:
await self.send_message(
chat_id=await config_get("chat_id", "reports"),
text=f"Bot stopped with PID `{getpid()}`",
)
except BadRequest:
logger.warning("Unable to send message to report chat.")
await super().stop()
logger.warning(f"Bot stopped with PID {getpid()}.")

View File

@@ -1,49 +1,100 @@
# -*- coding: utf-8 -*-
import os
import traceback
from functions import *
from bs4 import BeautifulSoup
import logging
from os import makedirs, path
from subprocess import check_output
from traceback import format_exc
from uuid import uuid4
from bs4 import BeautifulSoup
from libbot import config_get
from modules.utils import *
logger = logging.getLogger(__name__)
config = jsonLoad("config.json")
class EmptyCardException(Exception):
pass
async def getWaterLeft(cardid, filename, app=None):
async def getWaterLeft(cardid, filename, app=None):
url = f"https://bwtaqua.com.ua/card-topup/?id={cardid}"
try:
# if path.exists(f"data/pages/{str(filename)}.html") is False:
# run(["touch", f"data/pages/{str(filename)}.html"])
os.system(f'touch data/pages/{str(filename)}.html')
os.system(f'PageSaver/pageSaver "https://bwtaqua.com.ua/card-topup/?id={cardid}" > data/pages/{str(filename)}.html')
logger.info(f"Trying to get liters for url '{url}'")
with open(f'data/pages/{str(filename)}.html') as f:
html_file = f.read()
f.close()
if await config_get("use_compiled_page_saver") is True:
proc = check_output(
[
"PageSaver/pageSaver",
f"https://bwtaqua.com.ua/card-topup/?id={cardid}",
]
) # , ">", f"data/pages/{str(filename)}.html"])
html_file = proc.decode("utf-8")
else:
proc = check_output(
[
"node",
"./PageSaver/pageSaver.js",
f"https://bwtaqua.com.ua/card-topup/?id={cardid}",
]
) # , ">", f"data/pages/{str(filename)}.html"])
html_file = proc.decode("utf-8")
soup = BeautifulSoup(html_file, 'html.parser')
# with open(f'data/pages/{str(filename)}.html') as f:
# html_file = f.read()
# f.close()
output = (soup.find_all("h3", class_="headline headline_center headline_pink js-payment-balance")[0].getText()).replace("Твій баланс ", "").replace(" л", "")
soup = BeautifulSoup(html_file, "html.parser")
appendLog(f"Parsed {output} liters of water remaining (user: {str(filename)}, cardid: {cardid})")
output = (
(
soup.find_all(
"h3",
class_="headline headline_center headline_pink js-payment-balance",
)[0].getText()
)
.replace("Твій баланс ", "")
.replace(" л", "")
)
logger.info(
f"Parsed {output} liters of water remaining (user: {str(filename)}, cardid: {cardid})"
)
except Exception as exp:
logger.exception(
f"Exception occured: {exp} (user: {str(filename)}, cardid: {cardid})", exp
)
appendLog(f"Exception occured: {exp} (user: {str(filename)}, cardid: {cardid})")
try:
tmp_name = str(uuid4())
makedirs("tmp", exist_ok=True)
with open(path.join("tmp", tmp_name), "w", encoding="utf-8") as f:
f.write(html_file)
except NameError:
tmp_name = "N/A"
logger.warning(f"'html_file' is not defined so I won't gather any tmp data")
if app != None:
await app.send_message(config["owner_id"], f"**Exception occured:**\n • User: `{str(filename)}`\n • Card: [{cardid}]({url})\n • Exception: `{exp}`\n • Traceback: `{traceback.format_exc()}`", disable_web_page_preview=True)
await app.send_message(
await config_get("owner_id"),
f"**Exception occured:**\n • User: `{str(filename)}`\n • Card: [{cardid}]({url})\n • Exception: `{exp}`\n • TMP UUID: `{tmp_name}`\n • Traceback: `{format_exc()}`",
disable_web_page_preview=True,
)
else:
print(f'Exception occured and could not send to user: {exp}')
logger.warning(f"Exception occurred and could not send to user: {exp}")
output = "Failure"
return output
if __name__ == "__main__":
if __name__ == "__main__":
cardid = input("Enter card number: ")
userid = input("Enter Telegram ID (optional): ")

View File

@@ -1,22 +0,0 @@
RESET = '\u001b[0m'
BLACK = '\u001b[30m'
RED = '\u001b[31m'
GREEN = '\u001b[32m'
YELLOW = '\u001b[33m'
BLUE = '\u001b[34m'
MAGENTA = '\u001b[35m'
CYAN = '\u001b[36m'
WHITE = '\u001b[37m'
BBLACK = '\u001b[30;1m'
BRED = '\u001b[31;1m'
BGREEN = '\u001b[32;1m'
BYELLOW = '\u001b[33;1m'
BBLUE = '\u001b[34;1m'
BMAGENTA = '\u001b[35;1m'
BCYAN = '\u001b[36;1m'
BWHITE = '\u001b[37;1m'
ULINE = '\u001b[4m'
REVERSE = '\u001b[7m'

35
modules/utils.py Normal file
View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
from libbot import json_read, sync
async def string(key: str, *args: str, userlocale="uk"):
locales = await json_read("strings.json")
strings = locales[userlocale]
string = strings
for dict_key in args:
string = string[dict_key]
return string[key]
def userSet(userid, key: str, value):
database = sync.json_read("data/database.json")
if str(userid) not in database:
database[str(userid)] = {}
database[str(userid)][key] = value
sync.json_write(database, "data/database.json")
def userReset(userid, key: str):
database = sync.json_read("data/database.json")
del database[str(userid)][key]
sync.json_write(database, "data/database.json")
def userGet(userid, key: str):
try:
return sync.json_read("data/database.json")[str(userid)][key]
except KeyError:
return None
except FileNotFoundError:
return None

63
plugins/balance.py Normal file
View File

@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
import logging
from traceback import format_exc
from libbot import config_get, json_read
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.enums.chat_action import ChatAction
from pyrogram.types import Message
from modules.utils import string, userGet
from modules.bwt import getWaterLeft
logger = logging.getLogger(__name__)
@Client.on_message(
~filters.scheduled & filters.command(["balance", "баланс"], prefixes=["/", ""]) # type: ignore
)
async def command_balance(app: Client, msg: Message):
try:
if "card" in (await json_read("data/database.json"))[str(msg.from_user.id)]:
await app.send_chat_action(chat_id=msg.chat.id, action=ChatAction.TYPING)
water_left = await getWaterLeft(
userGet(msg.from_user.id, "card"), msg.from_user.id, app
)
if water_left == "":
await msg.reply_text(
(await string("error_new")).format(
f'https://bwtaqua.com.ua/card-topup/?id={userGet(msg.from_user.id, "card")}'
)
)
# raise EmptyCardException("Card information is empty")
elif water_left == "Failure":
await msg.reply_text(
(await string("error_occured")).format(await string("get_number"))
)
logger.warning(
f"User {str(msg.from_user.id)} could not get left water amount"
)
else:
await msg.reply_text((await string("card_balance")).format(water_left))
logger.info(
f"User {str(msg.from_user.id)} has {water_left} liters remaining"
)
else:
await msg.reply_text(
(await string("card_not_linked")).format(await string("get_number"))
)
logger.info(
f"User {str(msg.from_user.id)} tried to get balance without card set"
)
except Exception as exp:
if msg.from_user.id != await config_get("owner_id"):
await msg.reply_text(
(await string("error_occured")).format(await string("get_number"))
)
await app.send_message(
await config_get("owner_id"),
f"Error occured by {str(msg.from_user.id)}:\nException: `{exp}`\nTraceback: `{format_exc()}`",
)
logger.warning(f"User {str(msg.from_user.id)} could not get left water amount")

24
plugins/help.py Normal file
View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
import logging
from libbot import json_read
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import Message
from modules.utils import string, userSet
logger = logging.getLogger(__name__)
@Client.on_message(
~filters.scheduled
& filters.command(["start", "help", "допомога"], prefixes=["/", ""]) # type: ignore
)
async def command_help(app: Client, msg: Message):
await msg.reply_text((await string("welcome")).format(await string("get_number")))
if msg.from_user.language_code in await json_read("strings.json"):
userSet(msg.from_user.id, "locale", msg.from_user.language_code)
else:
userSet(msg.from_user.id, "locale", "en")

28
plugins/resetcard.py Normal file
View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
import logging
from libbot import json_read
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import Message
from modules.utils import string, userReset
logger = logging.getLogger(__name__)
@Client.on_message(
~filters.scheduled
& filters.command(["resetcard", "забути картку"], prefixes=["/", ""]) # type: ignore
)
async def command_resetcard(app: Client, msg: Message):
if "card" in (await json_read("data/database.json"))[str(msg.from_user.id)]:
userReset(msg.from_user.id, "card")
await msg.reply_text(await string("card_unlinked"))
logger.info(f"User {str(msg.from_user.id)} reseted his card")
else:
await msg.reply_text(
(await string("card_not_linked")).format(await string("get_number"))
)
logger.info(f"User {str(msg.from_user.id)} tried to reset non-existent card")

35
plugins/setcard.py Normal file
View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
import logging
from convopyro import listen_message
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import ForceReply, Message, ReplyKeyboardRemove
from modules.utils import string, userSet
logger = logging.getLogger(__name__)
@Client.on_message(
~filters.scheduled
& filters.command(["setcard", "задать карту"], prefixes=["/", ""]) # type: ignore
)
async def command_setcard(app: Client, msg: Message):
await msg.reply_text(
await string("send_number"),
reply_markup=ForceReply(placeholder=await string("enter_number")),
)
answer = await listen_message(app, msg.chat.id, timeout=None)
if answer is None:
return
elif answer.text.strip() in ["/cancel", "cancel", "/відміна", "відміна"]:
await msg.reply_text(await string("cancel"), reply_markup=ReplyKeyboardRemove())
return
userSet(answer.from_user.id, "card", answer.text)
logger.info(f"User {str(msg.from_user.id)} set card id to {answer.text}")
await msg.reply_text(
(await string("card_linked")).format(answer.text),
reply_markup=ReplyKeyboardRemove(),
)

21
plugins/shutdown.py Normal file
View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
import logging
from os import getpid
from libbot import config_get
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import Message
logger = logging.getLogger(__name__)
@Client.on_message(
~filters.scheduled & filters.command(["kill", "die", "shutdown"], prefixes="/") # type: ignore
)
async def command_shutdown(app: Client, msg: Message):
if msg.from_user.id == await config_get("owner_id"):
await msg.reply_text(f"Shutting down bot with pid **{getpid()}**")
logger.info(f"Shutting down as requested by {msg.from_user.id}")
exit()

36
plugins/topup.py Normal file
View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
import logging
from libbot import json_read
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.enums.chat_action import ChatAction
from pyrogram.types import Message
from modules.utils import string, userGet
logger = logging.getLogger(__name__)
@Client.on_message(
~filters.scheduled
& filters.command(["topup", "refill", "поповнити"], prefixes=["/", ""]) # type: ignore
)
async def command_topup(app: Client, msg: Message):
try:
if "card" in (await json_read("data/database.json"))[str(msg.from_user.id)]:
await app.send_chat_action(chat_id=msg.chat.id, action=ChatAction.TYPING)
await msg.reply_text(
(await string("top_up")).format(str(userGet(msg.from_user.id, "card")))
)
logger.info(f"User {str(msg.from_user.id)} requested top up")
else:
await msg.reply_text(
(await string("card_not_linked")).format(await string("get_number"))
)
logger.info(
f"User {str(msg.from_user.id)} tried to request top up without card set"
)
except Exception as exp:
await msg.reply_text(str(exp))

View File

@@ -1 +1,9 @@
beautifulsoup4
beautifulsoup4==4.12.2
convopyro==0.5
pathlib~=1.0.1
pyrogram==2.0.106
tgcrypto==1.2.5
ujson==5.8.0
uvloop==0.17.0
--extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple
libbot[speed,pyrogram]==1.4

View File

@@ -4,15 +4,14 @@
"get_number": "**Get card number (Var. 1):**\nOn the front bottom side of your card, number may be found\n\n**Get card number (Var. 2):**\n1. Scan QR on the card\n2. Open webpage from code\n3. Numer should be found in **Номер карти \"Здорова Вода\"** or **Номер карти BWT Aqua** fields",
"card_linked": "Linked card: `{0}`\n\nPlease, make sure the number is correct before using the bot",
"card_unlinked": "Card was unlinked from your Telegram",
"card_not_linked": "You don't have any linked card.\n\nВы можете задать её с помощью команды /setcard\n\n{0}",
"card_not_linked": "You don't have any linked card.\n\nВYou can set it using /setcard\n\n{0}",
"error_occured": "An error occurred while getting the amount of remaining water on the card.\n\nPlease make sure the linked card number is correct. If you are sure that the bot is broken, please contact @profitroll.\n\nLink your card: /setcard\n\n{0}",
"error_new": "An error occurred while getting the amount of remaining water on the card.\n\nLast a few weeks BWT seems to return empty string to balance request from our server. We assume that our server has been blacklisted.\n\nTo check your balance you can use official [BWT App](https://bwtaqua.com.ua/en/#app) or simply bookmark this page: {0}.",
"card_balance": "Card's balance is {0} l. of water",
"top_up": "[Click here to top up](https://bwtaqua.com.ua/card-topup/?id={0})",
"cancel": "Operation cancelled",
"cancel_none": "Nothing to cancel",
"cancel_first": "Operation ongoing. Cancel the current one using /cancel to run this action",
"enter_number": "Enter card number",
"send_number": "Please, send your card number"
"send_number": "Please, send your card number\nIf you want to abort this operation, use /cancel"
},
"uk": {
"welcome": "Привіт-привіт!\n\nЦей бот дозволяє дізнатись скільки літрів залишилось на вашій карточці.\n\n**Команди:**\n • /balance дізнатись баланс карти\n • /setcard приав'язати карту\n • /resetcard відв'язати карту\n\n{0}\n\nРозробник **не має жодного відношення до BWT Aqua**, а бот створений лише для особистого, некомерційного використання.",
@@ -21,12 +20,11 @@
"card_unlinked": "Картку відв'язано від вашого Telegram",
"card_not_linked": "У вас немає прив'язаної картки.\n\nВи можете зробити це за допомогою команди /setcard\n\n{0}",
"error_occured": "При отриманні води на карточці виникла помилка.\n\nБудь ласка, упевніться що номер карти правильний. Якщо ви впевнені, що номер картки правильний та бот зламався зв'яжіться з @profitroll.\n\nПрив'язати карту: /setcard\n\n{0}",
"error_new": "При отриманні води на карточці виникла помилка.\n\nОстанні тижні BWT повертає нашому серверу порожні строки замість балансу. Є підозри, що сервер потрапив у блеклист.\n\nДля перевірки балансу рекомендуємо користуватись офіційним [додатком BWT](https://bwtaqua.com.ua/#app) або просто додати цю сторінку у закладки: {0}.",
"card_balance": "На карточці {0} л. води",
"top_up": "[Натисніть для поповнення](https://bwtaqua.com.ua/card-topup/?id={0})",
"cancel": "Операція відмінена",
"cancel_none": "Нема що відміняти",
"cancel_first": "Триває інша операція. Відмініть триваючу операцію командою /cancel щоб запустити іншу дію",
"cancel": "Операцію скасовано",
"enter_number": "Введіть номер картки",
"send_number": "Будь ласка, надішліть номер вашої картки"
"send_number": "Будь ласка, надішліть номер вашої картки\nЯкщо ви хочете скасувати цю операцію, використовуйте /cancel"
}
}