Compare commits
10
Commits
9959ee74d5
...
sql-db
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d81f669e3a | ||
|
|
41a1dab913 | ||
|
|
22e55a90bb | ||
|
|
981cc3d1f9 | ||
|
|
fc60827261 | ||
|
|
36aeb7f682 | ||
|
|
3b29315129 | ||
|
|
0e7c185517 | ||
|
|
b898950f11 | ||
|
|
d7f499e5c6 |
+3
-1
@@ -1,3 +1,5 @@
|
|||||||
config.yaml
|
config.yaml
|
||||||
database/*
|
database/*
|
||||||
__pycache__/*
|
__pycache__/*
|
||||||
|
old.py
|
||||||
|
test.py
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# TG Keeper
|
||||||
|
|
||||||
|
TG Keeper bot allows you to store notes, display them at a specified interval, and recognizes date formats: DD.MM, DD month. It works both in group chats by mentioning @username_bot and in private messages.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Convenient setup interface
|
||||||
|
- Dependency installation via `requirements.txt`
|
||||||
|
- Initial configuration in the `config.yaml` file
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/yourusername/tg-keeper.git
|
||||||
|
```
|
||||||
|
2. Navigate to the project directory:
|
||||||
|
```sh
|
||||||
|
cd tg-keeper
|
||||||
|
```
|
||||||
|
3. Install the dependencies:
|
||||||
|
```sh
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
1. Open the `config.yaml` file and configure the parameters as you wish.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Run the bot:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python bot.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can add and manage your notes through TG Keeper.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
admin_id: "856850518"
|
||||||
|
api_token: null
|
||||||
|
test_token: null
|
||||||
|
bot_username: "@keeper_inbot"
|
||||||
|
users_path: "database/users.yaml"
|
||||||
|
user_path: "database/users/{}.yaml"
|
||||||
@@ -1,178 +1,170 @@
|
|||||||
import yaml, os
|
import yaml, os
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from telebot import types
|
from telebot import types
|
||||||
|
from psycopg2 import pool
|
||||||
|
from sshtunnel import SSHTunnelForwarder
|
||||||
|
|
||||||
|
connection_pool: pool.SimpleConnectionPool = None
|
||||||
|
|
||||||
config = yaml.safe_load(open("config.yaml"))
|
config = yaml.safe_load(open("config.yaml"))
|
||||||
users_path = config["users_path"]
|
|
||||||
user_path = config["user_path"]
|
server = SSHTunnelForwarder(
|
||||||
|
(config["host"], 22),
|
||||||
|
ssh_private_key="~/.ssh/id_rsa",
|
||||||
|
ssh_username="krosh",
|
||||||
|
remote_bind_address=("localhost", config["sql_port"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.start()
|
||||||
|
connection_pool = pool.SimpleConnectionPool(
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
database=config["sql_database"],
|
||||||
|
user=config["sql_user"],
|
||||||
|
password=config["sql_password"],
|
||||||
|
host="localhost",
|
||||||
|
port=server.local_bind_port,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def into_dict(curs) -> dict:
|
||||||
|
cols = [desc[0] for desc in curs.description]
|
||||||
|
data = curs.fetchone()
|
||||||
|
|
||||||
|
return dict(zip(cols, data)) if data else None
|
||||||
|
|
||||||
|
|
||||||
|
def into_list(curs) -> list[dict]:
|
||||||
|
cols = [desc[0] for desc in curs.description]
|
||||||
|
data = curs.fetchall()
|
||||||
|
|
||||||
|
return [dict(zip(cols, row)) for row in data] if data else None
|
||||||
|
|
||||||
|
|
||||||
def check_user(user_id: int) -> dict:
|
def check_user(user_id: int) -> dict:
|
||||||
users = yaml.safe_load(open(users_path, "r", encoding="utf-8"))
|
con = connection_pool.getconn()
|
||||||
|
curs = con.cursor()
|
||||||
|
curs.execute("SELECT * FROM users WHERE id = %s", (user_id,))
|
||||||
|
|
||||||
if not users:
|
user = into_dict(curs)
|
||||||
users = {}
|
connection_pool.putconn(con)
|
||||||
|
|
||||||
if user_id in users:
|
return user
|
||||||
return users[user_id]
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def new_user(message: types.Message) -> bool:
|
def new_user(message: types.Message) -> bool:
|
||||||
if check_user(message.chat.id):
|
if check_user(message.chat.id):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
user = {
|
con = connection_pool.getconn()
|
||||||
"id": message.chat.id,
|
curs = con.cursor()
|
||||||
"type": message.chat.type,
|
curs.execute(
|
||||||
"username": message.chat.username,
|
"""
|
||||||
"first_name": message.chat.first_name,
|
INSERT INTO users (id, username, firstname, lastname, chat)
|
||||||
"last_name": message.chat.last_name,
|
VALUES (%s, %s, %s, %s, %s)
|
||||||
"time_created": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
""",
|
||||||
"last_message": None,
|
(
|
||||||
"remind_delta": 12 * 60 * 60, # 12 часов по умолчанию
|
message.chat.id,
|
||||||
}
|
message.chat.username,
|
||||||
|
message.chat.first_name,
|
||||||
users = yaml.safe_load(open(users_path, "r"), encoding="utf-8")
|
message.chat.last_name,
|
||||||
|
message.chat.type,
|
||||||
if not users:
|
),
|
||||||
users = {}
|
|
||||||
|
|
||||||
users[user["id"]] = user
|
|
||||||
|
|
||||||
yaml_data = yaml.dump(
|
|
||||||
users,
|
|
||||||
default_flow_style=False,
|
|
||||||
encoding="utf-8",
|
|
||||||
allow_unicode=True,
|
|
||||||
width=float("inf"),
|
|
||||||
sort_keys=False,
|
|
||||||
)
|
)
|
||||||
|
con.commit()
|
||||||
|
connection_pool.putconn(con)
|
||||||
|
|
||||||
with open(users_path, "wb") as file:
|
return
|
||||||
file.write(yaml_data)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def add_note(
|
def add_note(
|
||||||
id: int, text: str, time_notif: datetime = None, list: str = "Default"
|
id: int, text: str, remind_at: datetime = None, list: str = "Default"
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if not check_user(id):
|
if not check_user(id):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
notes = (
|
con = connection_pool.getconn()
|
||||||
yaml.safe_load(open(user_path.format(id), "r", encoding="utf-8"))
|
curs = con.cursor()
|
||||||
if os.path.exists(user_path.format(id))
|
curs.execute(
|
||||||
else {}
|
"""
|
||||||
|
SELECT id FROM lists WHERE name = %s AND user_id = %s
|
||||||
|
""",
|
||||||
|
(list, id),
|
||||||
)
|
)
|
||||||
|
|
||||||
if list not in notes:
|
data = curs.fetchone()
|
||||||
notes[list] = {"name": list, "description": "Стандартный список", "notes": []}
|
|
||||||
|
|
||||||
notes[list]["notes"].append(
|
if not data:
|
||||||
{
|
curs.execute(
|
||||||
"text": text,
|
"""
|
||||||
"time_notif": str(time_notif) if time_notif else None,
|
INSERT INTO lists (name, user_id)
|
||||||
"time_created": str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
|
VALUES (%s, %s)
|
||||||
}
|
""",
|
||||||
)
|
(list, id),
|
||||||
|
|
||||||
if True: # TODO: edit sorting
|
|
||||||
import functools
|
|
||||||
|
|
||||||
def comp(x, y):
|
|
||||||
if not x["time_notif"] and not y["time_notif"]:
|
|
||||||
return 0
|
|
||||||
elif not x["time_notif"]:
|
|
||||||
return -1
|
|
||||||
elif not y["time_notif"]:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
x_time = datetime.strptime(
|
|
||||||
x["time_notif"],
|
|
||||||
"%Y-%m-%d %H:%M:%S",
|
|
||||||
)
|
|
||||||
y_time = datetime.strptime(
|
|
||||||
y["time_notif"],
|
|
||||||
"%Y-%m-%d %H:%M:%S",
|
|
||||||
)
|
|
||||||
if x_time > y_time:
|
|
||||||
return 1
|
|
||||||
elif x_time < y_time:
|
|
||||||
return -1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
notes[list]["notes"].sort(
|
|
||||||
key=lambda x: datetime.strptime(
|
|
||||||
x["time_created"],
|
|
||||||
"%Y-%m-%d %H:%M:%S",
|
|
||||||
),
|
|
||||||
reverse=True,
|
|
||||||
)
|
)
|
||||||
|
con.commit()
|
||||||
|
|
||||||
notes[list]["notes"].sort(
|
curs.execute(
|
||||||
key=functools.cmp_to_key(comp),
|
"""
|
||||||
)
|
INSERT INTO notes (user_id, list_id, content, remind_at)
|
||||||
|
VALUES (%s, (SELECT id FROM lists WHERE name = %s AND user_id = %s), %s, %s)
|
||||||
yaml_data = yaml.dump(
|
""",
|
||||||
notes,
|
(id, list, id, text, remind_at),
|
||||||
default_flow_style=False,
|
|
||||||
encoding="utf-8",
|
|
||||||
allow_unicode=True,
|
|
||||||
width=float("inf"),
|
|
||||||
sort_keys=False,
|
|
||||||
)
|
)
|
||||||
|
con.commit()
|
||||||
|
rowcnt = curs.rowcount
|
||||||
|
|
||||||
with open(user_path.format(id), "wb") as file:
|
connection_pool.putconn(con)
|
||||||
file.write(yaml_data)
|
|
||||||
|
|
||||||
return True
|
return True if rowcnt else False
|
||||||
|
|
||||||
|
|
||||||
def get_notes(id: int, list="Default") -> dict:
|
def get_notes(id: int, list="Default") -> list[dict]:
|
||||||
if not check_user(id):
|
if not check_user(id):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not os.path.exists(user_path.format(id)):
|
con = connection_pool.getconn()
|
||||||
return None
|
curs = con.cursor()
|
||||||
|
|
||||||
notes = yaml.safe_load(open(user_path.format(id), "r", encoding="utf-8"))
|
curs.execute(
|
||||||
|
"""
|
||||||
|
SELECT * FROM notes
|
||||||
|
WHERE user_id = %s AND list_id = (SELECT id FROM lists WHERE name = %s AND user_id = %s)
|
||||||
|
ORDER BY remind_at IS NULL DESC, remind_at ASC, created_at DESC
|
||||||
|
""",
|
||||||
|
(id, list, id),
|
||||||
|
)
|
||||||
|
|
||||||
return notes[list] if list in notes else None
|
notes = into_list(curs)
|
||||||
|
connection_pool.putconn(con)
|
||||||
|
|
||||||
|
return notes
|
||||||
|
|
||||||
|
|
||||||
def delete_note(id, note_ind, list="Default") -> bool:
|
def delete_note(id, note_ind, list="Default") -> bool:
|
||||||
if not check_user(id):
|
if not check_user(id):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not os.path.exists(user_path.format(id)):
|
con = connection_pool.getconn()
|
||||||
return False
|
curs = con.cursor()
|
||||||
|
curs.execute(
|
||||||
notes = yaml.safe_load(open(user_path.format(id), "r", encoding="utf-8"))
|
"""
|
||||||
|
DELETE FROM notes
|
||||||
if list not in notes:
|
WHERE id = (
|
||||||
return False
|
SELECT id FROM notes
|
||||||
|
WHERE user_id = %s AND list_id = (SELECT id FROM lists WHERE name = %s AND user_id = %s)
|
||||||
if note_ind >= len(notes[list]["notes"]):
|
ORDER BY remind_at IS NULL DESC, remind_at ASC, created_at DESC
|
||||||
return False
|
LIMIT 1 OFFSET %s
|
||||||
|
)
|
||||||
notes[list]["notes"].pop(note_ind)
|
""",
|
||||||
|
(id, list, id, note_ind),
|
||||||
yaml_data = yaml.dump(
|
|
||||||
notes,
|
|
||||||
default_flow_style=False,
|
|
||||||
encoding="utf-8",
|
|
||||||
allow_unicode=True,
|
|
||||||
width=float("inf"),
|
|
||||||
sort_keys=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(user_path.format(id), "wb") as file:
|
con.commit()
|
||||||
file.write(yaml_data)
|
rowcnt = curs.rowcount
|
||||||
|
connection_pool.putconn(con)
|
||||||
|
|
||||||
return True
|
return True if rowcnt else False
|
||||||
|
|
||||||
|
|
||||||
def new_message(message: types.Message) -> int:
|
def new_message(message: types.Message) -> int:
|
||||||
@@ -182,54 +174,98 @@ def new_message(message: types.Message) -> int:
|
|||||||
if not check_user(id):
|
if not check_user(id):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
users = yaml.safe_load(open(users_path, "r", encoding="utf-8"))
|
con = connection_pool.getconn()
|
||||||
|
curs = con.cursor()
|
||||||
user = users[id]
|
curs.execute(
|
||||||
|
"""
|
||||||
if "last_message" not in user:
|
SELECT last_message FROM users WHERE id = %s
|
||||||
user["last_message"] = None
|
""",
|
||||||
|
(id,),
|
||||||
last_id = user["last_message"]
|
|
||||||
user["last_message"] = new_msg_id
|
|
||||||
|
|
||||||
users[id] = user
|
|
||||||
|
|
||||||
yaml_data = yaml.dump(
|
|
||||||
users,
|
|
||||||
default_flow_style=False,
|
|
||||||
encoding="utf-8",
|
|
||||||
allow_unicode=True,
|
|
||||||
width=float("inf"),
|
|
||||||
sort_keys=False,
|
|
||||||
)
|
)
|
||||||
|
last_id = curs.fetchone()[0]
|
||||||
|
|
||||||
with open(users_path, "wb") as file:
|
curs.execute(
|
||||||
file.write(yaml_data)
|
"""
|
||||||
|
UPDATE users
|
||||||
|
SET last_message = %s
|
||||||
|
WHERE id = %s
|
||||||
|
""",
|
||||||
|
(new_msg_id, id),
|
||||||
|
)
|
||||||
|
con.commit()
|
||||||
|
connection_pool.putconn(con)
|
||||||
|
|
||||||
return last_id
|
return last_id
|
||||||
|
|
||||||
|
|
||||||
def check_old_notes() -> list[int, str, int]: # возвращает первую устаревшую заметку
|
def check_old_notes() -> list[int, str, int]: # возвращает первую устаревшую заметку
|
||||||
users = yaml.safe_load(open(users_path, "r", encoding="utf-8"))
|
con = connection_pool.getconn()
|
||||||
|
curs = con.cursor()
|
||||||
|
curs.execute(
|
||||||
|
"""
|
||||||
|
SELECT id FROM users
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
users = curs.fetchall()
|
||||||
|
|
||||||
for user_id in users.keys():
|
for user_id in users:
|
||||||
notes = yaml.safe_load(open(user_path.format(user_id), "r", encoding="utf-8"))
|
curs.execute(
|
||||||
|
"""
|
||||||
|
SELECT * FROM notes
|
||||||
|
WHERE id = %s
|
||||||
|
ORDER BY remind_at IS NULL DESC, remind_at ASC, created_at DESC
|
||||||
|
""",
|
||||||
|
(user_id,),
|
||||||
|
)
|
||||||
|
notes = [dict(row) for row in curs.fetchall()]
|
||||||
|
|
||||||
if "remind_delta" not in users[user_id]:
|
if not notes:
|
||||||
users[user_id]["remind_delta"] = 12 * 60 * 60 # не сохранится!
|
continue
|
||||||
|
|
||||||
timedt = timedelta(seconds=users[user_id]["remind_delta"])
|
timedt = timedelta(seconds=user_settings(user_id)["remind_delta"])
|
||||||
|
|
||||||
for list in notes.keys():
|
for ind in range(len(notes)):
|
||||||
for ind in range(len(notes[list]["notes"])):
|
if (
|
||||||
if (
|
notes[ind]["remind_at"]
|
||||||
notes[list]["notes"][ind]["time_notif"]
|
and datetime.strptime(notes[ind]["remind_at"], "%Y-%m-%d %H:%M:%S")
|
||||||
and datetime.strptime(
|
- timedt
|
||||||
notes[list]["notes"][ind]["time_notif"], "%Y-%m-%d %H:%M:%S"
|
< datetime.now()
|
||||||
)
|
):
|
||||||
- timedt
|
curs.execute(
|
||||||
< datetime.now()
|
"""
|
||||||
):
|
SELECT name FROM lists
|
||||||
return user_id, list, ind
|
WHERE id = %s
|
||||||
|
""",
|
||||||
|
(notes[ind]["list_id"],),
|
||||||
|
)
|
||||||
|
list_name = curs.fetchone()
|
||||||
|
connection_pool.putconn(con)
|
||||||
|
|
||||||
|
return (user_id, list_name, ind)
|
||||||
|
|
||||||
|
connection_pool.putconn(con)
|
||||||
|
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
|
|
||||||
|
def update_user_settings(user_id: int, param: str, value: any) -> bool:
|
||||||
|
con = connection_pool.getconn()
|
||||||
|
curs = con.cursor()
|
||||||
|
query = f"UPDATE users SET {param} = %s WHERE id = %s"
|
||||||
|
curs.execute(query, (value, user_id))
|
||||||
|
con.commit()
|
||||||
|
rowcnt = curs.rowcount
|
||||||
|
connection_pool.putconn(con)
|
||||||
|
|
||||||
|
return True if rowcnt else False
|
||||||
|
|
||||||
|
|
||||||
|
def user_settings(user_id: int) -> dict:
|
||||||
|
con = connection_pool.getconn()
|
||||||
|
curs = con.cursor()
|
||||||
|
curs.execute("SELECT * FROM users WHERE id = %s", (user_id,))
|
||||||
|
|
||||||
|
user = into_dict(curs)
|
||||||
|
connection_pool.putconn(con)
|
||||||
|
|
||||||
|
return user
|
||||||
|
|||||||
@@ -12,3 +12,81 @@
|
|||||||
error_message: "Произошла ошибка. \
|
error_message: "Произошла ошибка. \
|
||||||
\nПожалуйста, попробуйте снова или свяжитесь с @Kr0sH_512"
|
\nПожалуйста, попробуйте снова или свяжитесь с @Kr0sH_512"
|
||||||
no_reminders: "У вас нет напоминаний."
|
no_reminders: "У вас нет напоминаний."
|
||||||
|
not_registered: "Пожалуйста, воспользуйтесь сперва командой /start"
|
||||||
|
|
||||||
|
"list_reaction":
|
||||||
|
[
|
||||||
|
"👍",
|
||||||
|
"👎",
|
||||||
|
"❤",
|
||||||
|
"🔥",
|
||||||
|
"🥰",
|
||||||
|
"👏",
|
||||||
|
"😁",
|
||||||
|
"🤔",
|
||||||
|
"🤯",
|
||||||
|
"😱",
|
||||||
|
"🤬",
|
||||||
|
"😢",
|
||||||
|
"🎉",
|
||||||
|
"🤩",
|
||||||
|
"🤮",
|
||||||
|
"💩",
|
||||||
|
"🙏",
|
||||||
|
"👌",
|
||||||
|
"🕊",
|
||||||
|
"🤡",
|
||||||
|
"🥱",
|
||||||
|
"🥴",
|
||||||
|
"😍",
|
||||||
|
"🐳",
|
||||||
|
"❤🔥",
|
||||||
|
"🌚",
|
||||||
|
"🌭",
|
||||||
|
"💯",
|
||||||
|
"🤣",
|
||||||
|
"⚡",
|
||||||
|
"🍌",
|
||||||
|
"🏆",
|
||||||
|
"💔",
|
||||||
|
"🤨",
|
||||||
|
"😐",
|
||||||
|
"🍓",
|
||||||
|
"🍾",
|
||||||
|
"💋",
|
||||||
|
"🖕",
|
||||||
|
"😈",
|
||||||
|
"😴",
|
||||||
|
"😭",
|
||||||
|
"🤓",
|
||||||
|
"👻",
|
||||||
|
"👨💻",
|
||||||
|
"👀",
|
||||||
|
"🎃",
|
||||||
|
"🙈",
|
||||||
|
"😇",
|
||||||
|
"😨",
|
||||||
|
"🤝",
|
||||||
|
"✍",
|
||||||
|
"🤗",
|
||||||
|
"🫡",
|
||||||
|
"🎅",
|
||||||
|
"🎄",
|
||||||
|
"☃",
|
||||||
|
"💅",
|
||||||
|
"🤪",
|
||||||
|
"🗿",
|
||||||
|
"🆒",
|
||||||
|
"💘",
|
||||||
|
"🙉",
|
||||||
|
"🦄",
|
||||||
|
"😘",
|
||||||
|
"💊",
|
||||||
|
"🙊",
|
||||||
|
"😎",
|
||||||
|
"👾",
|
||||||
|
"🤷♂",
|
||||||
|
"🤷",
|
||||||
|
"🤷♀",
|
||||||
|
"😡",
|
||||||
|
]
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
CREATE type chat_type AS ENUM ('private', 'group', 'supergroup', 'channel');
|
||||||
|
|
||||||
|
CREATE TABLE users (
|
||||||
|
id BIGINT PRIMARY KEY,
|
||||||
|
username TEXT,
|
||||||
|
firstname TEXT,
|
||||||
|
lastname TEXT,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
last_message BIGINT NOT NULL DEFAULT 0,
|
||||||
|
remind_delta BIGINT NOT NULL DEFAULT 21600,
|
||||||
|
chat chat_type NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE Table lists (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id BIGINT NOT NULL REFERENCES users (id),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
discription TEXT,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE notes (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id BIGINT NOT NULL REFERENCES users (id),
|
||||||
|
list_id BIGINT NOT NULL REFERENCES lists (id),
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
remind_at TIMESTAMP,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pyyaml
|
||||||
|
pytelegrambotapi
|
||||||
|
schedule
|
||||||
|
python-telegram-bot
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
#!/usr/bin/python3.3
|
#!/usr/bin/python3.3
|
||||||
import threading, telebot, schedule, time, yaml
|
import threading, telebot, schedule, time, yaml
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import os, sys, inspect
|
import os, sys, inspect, random
|
||||||
import re, locale
|
import re, locale
|
||||||
from telegram.constants import ParseMode
|
from telegram.constants import ParseMode
|
||||||
from telebot import types
|
from telebot import types
|
||||||
import for_db as db
|
import for_db as db
|
||||||
|
|
||||||
config = yaml.safe_load(open("config.yaml"))
|
config = yaml.safe_load(open("config.yaml"))
|
||||||
lang = yaml.safe_load(open("lang.yaml"))["ru"]
|
lang = yaml.safe_load(open("lang.yaml", encoding="utf-8"))["ru"]
|
||||||
|
list_reactions = yaml.safe_load(open("lang.yaml", encoding="utf-8"))["list_reaction"]
|
||||||
bot = telebot.TeleBot(
|
bot = telebot.TeleBot(
|
||||||
config["api_token"],
|
config["test_token"],
|
||||||
colorful_logs=True,
|
colorful_logs=True,
|
||||||
disable_web_page_preview=True,
|
disable_web_page_preview=True,
|
||||||
parse_mode=ParseMode.HTML,
|
parse_mode=ParseMode.HTML,
|
||||||
@@ -19,7 +20,6 @@ bot = telebot.TeleBot(
|
|||||||
admin_id = config["admin_id"]
|
admin_id = config["admin_id"]
|
||||||
bot_username = config["bot_username"]
|
bot_username = config["bot_username"]
|
||||||
|
|
||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, "ru_RU.UTF-8")
|
locale.setlocale(locale.LC_ALL, "ru_RU.UTF-8")
|
||||||
|
|
||||||
|
|
||||||
@@ -43,7 +43,10 @@ def restart_bot(message: types.Message):
|
|||||||
@bot.message_handler(commands=["start"])
|
@bot.message_handler(commands=["start"])
|
||||||
def start(message: types.Message):
|
def start(message: types.Message):
|
||||||
|
|
||||||
send_message(message, lang["start_message"])
|
send_message(message, lang["welcome_message"])
|
||||||
|
|
||||||
|
if not db.check_user(message.chat.id):
|
||||||
|
db.new_user(message)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -54,6 +57,9 @@ def help(message: types.Message):
|
|||||||
|
|
||||||
send_message(message, help_msg)
|
send_message(message, help_msg)
|
||||||
|
|
||||||
|
if not db.check_user(message.chat.id):
|
||||||
|
db.new_user(message)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@@ -70,13 +76,13 @@ def delete_notes_callback(call):
|
|||||||
note_ind = int(call.data.split("#")[2])
|
note_ind = int(call.data.split("#")[2])
|
||||||
prefix = call.data.split("#")[0]
|
prefix = call.data.split("#")[0]
|
||||||
|
|
||||||
note = db.get_notes(call.message.chat.id, prefix)["notes"][note_ind]
|
note = db.get_notes(call.message.chat.id, prefix)[note_ind]
|
||||||
|
|
||||||
db.delete_note(call.message.chat.id, note_ind, prefix)
|
db.delete_note(call.message.chat.id, note_ind, prefix)
|
||||||
|
|
||||||
list_notes(call.message, prefix, edit=True)
|
list_notes(call.message, prefix, edit=True)
|
||||||
|
|
||||||
bot.send_message(call.message.chat.id, f"❌ Удалено:\n\n{note['text']}")
|
bot.send_message(call.message.chat.id, f"❌ Удалено:\n\n{note['content']}")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -85,7 +91,7 @@ def delete_notes_callback(call):
|
|||||||
def edit_notes_callback(call):
|
def edit_notes_callback(call):
|
||||||
note_ind = int(call.data.split("#")[2])
|
note_ind = int(call.data.split("#")[2])
|
||||||
prefix = call.data.split("#")[0]
|
prefix = call.data.split("#")[0]
|
||||||
notes = db.get_notes(call.message.chat.id, prefix)["notes"]
|
notes = db.get_notes(call.message.chat.id, prefix)
|
||||||
|
|
||||||
note = notes[note_ind]
|
note = notes[note_ind]
|
||||||
|
|
||||||
@@ -105,10 +111,10 @@ def edit_notes_callback(call):
|
|||||||
|
|
||||||
text = f"📝 <b>Заметка {note_ind + 1}</b>"
|
text = f"📝 <b>Заметка {note_ind + 1}</b>"
|
||||||
|
|
||||||
if note["time_notif"]:
|
if note["remind_at"]:
|
||||||
text += f" ⌚️ <b>{datetime.strptime(note['time_notif'], '%Y-%m-%d %H:%M:%S').strftime('%d %B %Y')}</b>"
|
text += f" ⌚️ <b>{note['remind_at'].strftime('%d %B %Y')}</b>"
|
||||||
|
|
||||||
text += f"\n\n{note['text']}"
|
text += f"\n\n{note['content']}"
|
||||||
|
|
||||||
bot.edit_message_text(
|
bot.edit_message_text(
|
||||||
text,
|
text,
|
||||||
@@ -126,7 +132,7 @@ def choose_note_callback(call):
|
|||||||
prefix = call.data.split("#")[0]
|
prefix = call.data.split("#")[0]
|
||||||
markup = types.InlineKeyboardMarkup()
|
markup = types.InlineKeyboardMarkup()
|
||||||
num_on_page = 6 # Лимит телеграмма - 8
|
num_on_page = 6 # Лимит телеграмма - 8
|
||||||
notes = db.get_notes(call.message.chat.id, prefix)["notes"]
|
notes = db.get_notes(call.message.chat.id, prefix)
|
||||||
|
|
||||||
max_page = (len(notes) - 1) // num_on_page
|
max_page = (len(notes) - 1) // num_on_page
|
||||||
lst_button = []
|
lst_button = []
|
||||||
@@ -187,13 +193,70 @@ def choose_note_callback(call):
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@bot.message_handler(commands=["settings", "setting", "edit"])
|
@bot.callback_query_handler(func=lambda call: "edit_time" in call.data)
|
||||||
def display_settings(message: types.Message):
|
def edit_time(call):
|
||||||
|
list_time = {
|
||||||
|
(24 - 8) * 3600: "8:00 в день до напоминания",
|
||||||
|
(24 - 12) * 3600: "12:00 в день до напоминания",
|
||||||
|
(24 - 18) * 3600: "18:00 в день до напоминания",
|
||||||
|
0: "0:00 в день до напоминания",
|
||||||
|
(-8) * 3600: "8:00 в день напоминания",
|
||||||
|
(-12) * 3600: "12:00 в день напоминания",
|
||||||
|
(-18) * 3600: "18:00 в день напоминания",
|
||||||
|
}
|
||||||
|
|
||||||
|
if call.data[-1] == "#":
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
for delta in list_time.keys():
|
||||||
|
markup.add(
|
||||||
|
types.InlineKeyboardButton(
|
||||||
|
list_time[delta],
|
||||||
|
callback_data=f"edit_time^{delta}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
bot.edit_message_text(
|
||||||
|
"⏰ Выберите время для уведомлений:",
|
||||||
|
call.message.chat.id,
|
||||||
|
call.message.message_id,
|
||||||
|
reply_markup=markup,
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
delta = int(call.data.split("^")[1])
|
||||||
|
|
||||||
|
db.update_user_settings(call.message.chat.id, "remind_delta", delta)
|
||||||
|
|
||||||
|
bot.edit_message_text(
|
||||||
|
f"Выбранное время: \n\n{list_time[delta]}",
|
||||||
|
call.message.chat.id,
|
||||||
|
call.message.message_id,
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@bot.message_handler(commands=["list"])
|
@bot.message_handler(commands=["settings", "setting", "edit"])
|
||||||
|
def display_settings(message: types.Message):
|
||||||
|
if not db.check_user(message.chat.id):
|
||||||
|
db.new_user(message)
|
||||||
|
|
||||||
|
settings_msg = "Изменить время отправки напоминания"
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(
|
||||||
|
types.InlineKeyboardButton(
|
||||||
|
"Изменить время",
|
||||||
|
callback_data="edit_time#",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
send_message(message, settings_msg, markup)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@bot.message_handler(commands=["list", "lists", "l"])
|
||||||
def list_notes(message: types.Message, list: str = "Default", edit: bool = False):
|
def list_notes(message: types.Message, list: str = "Default", edit: bool = False):
|
||||||
notes = db.get_notes(message.chat.id, list)
|
notes = db.get_notes(message.chat.id, list)
|
||||||
|
|
||||||
@@ -202,15 +265,13 @@ def list_notes(message: types.Message, list: str = "Default", edit: bool = False
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
list_notes = notes["notes"]
|
|
||||||
|
|
||||||
notes_msg = "⚡️ <u>Ваши напоминания</u>:\n\n"
|
notes_msg = "⚡️ <u>Ваши напоминания</u>:\n\n"
|
||||||
for i in range(len(list_notes)):
|
for i in range(len(notes)):
|
||||||
notes_msg += f"<b>{i + 1})</b> "
|
notes_msg += f"<b>{i + 1})</b> "
|
||||||
if list_notes[i]["time_notif"]:
|
if notes[i]["remind_at"]:
|
||||||
notes_msg += f" ⌚️ <b>{datetime.strptime(list_notes[i]['time_notif'], '%Y-%m-%d %H:%M:%S').strftime('%d %B %Y')}:</b>\n"
|
notes_msg += f" ⌚️ <b>{notes[i]['remind_at'].strftime('%d %B %Y')}:</b>\n"
|
||||||
|
|
||||||
notes_msg += f"{list_notes[i]['text']}\n\n"
|
notes_msg += f"{notes[i]['content']}\n\n"
|
||||||
|
|
||||||
markup = types.InlineKeyboardMarkup()
|
markup = types.InlineKeyboardMarkup()
|
||||||
markup.add(
|
markup.add(
|
||||||
@@ -228,6 +289,11 @@ def list_notes(message: types.Message, list: str = "Default", edit: bool = False
|
|||||||
|
|
||||||
msg = send_message(message, notes_msg, markup)
|
msg = send_message(message, notes_msg, markup)
|
||||||
|
|
||||||
|
reaction = types.ReactionTypeEmoji(random.choice(list_reactions))
|
||||||
|
bot.set_message_reaction(
|
||||||
|
message.chat.id, message.message_id, [reaction], is_big=True
|
||||||
|
)
|
||||||
|
|
||||||
old_msg_id = db.new_message(msg)
|
old_msg_id = db.new_message(msg)
|
||||||
|
|
||||||
if old_msg_id:
|
if old_msg_id:
|
||||||
@@ -242,14 +308,37 @@ def text_message(message: types.Message):
|
|||||||
|
|
||||||
return # ignore messages from supergroups
|
return # ignore messages from supergroups
|
||||||
|
|
||||||
if db.check_user(message.chat.id) is None:
|
if not db.check_user(message.chat.id):
|
||||||
db.new_user(message)
|
db.new_user(message)
|
||||||
|
|
||||||
message.text = message.text.replace(f"{bot_username}", "").strip()
|
|
||||||
if not message.text:
|
if not message.text:
|
||||||
|
return
|
||||||
|
|
||||||
|
msg_text = message.text
|
||||||
|
msg_entities = message.entities
|
||||||
|
|
||||||
|
if not message.text.replace(f"{bot_username}", "").strip():
|
||||||
if not message.reply_to_message:
|
if not message.reply_to_message:
|
||||||
return
|
return
|
||||||
message.text = message.reply_to_message.text
|
msg_text = message.reply_to_message.text
|
||||||
|
msg_entities = message.reply_to_message.entities
|
||||||
|
message = message.reply_to_message
|
||||||
|
|
||||||
|
if msg_entities:
|
||||||
|
text = msg_text
|
||||||
|
html_parts = []
|
||||||
|
last_idx = 0
|
||||||
|
for ent in msg_entities:
|
||||||
|
if ent.type == "text_link":
|
||||||
|
html_parts.append(text[last_idx : ent.offset])
|
||||||
|
link_text = text[ent.offset : ent.offset + ent.length]
|
||||||
|
html_parts.append(f'<a href="{ent.url}">{link_text}</a>')
|
||||||
|
last_idx = ent.offset + ent.length
|
||||||
|
|
||||||
|
html_parts.append(text[last_idx:])
|
||||||
|
msg_text = "".join(html_parts)
|
||||||
|
|
||||||
|
msg_text = msg_text.replace(f"{bot_username}", "").strip()
|
||||||
|
|
||||||
date_patterns = [
|
date_patterns = [
|
||||||
r"\b\d{1,2}\s(?:января|февраля|марта|апреля|мая|июня|июля|августа|сентября|октября|ноября|декабря)\b",
|
r"\b\d{1,2}\s(?:января|февраля|марта|апреля|мая|июня|июля|августа|сентября|октября|ноября|декабря)\b",
|
||||||
@@ -298,7 +387,7 @@ def text_message(message: types.Message):
|
|||||||
date_found_dt = None
|
date_found_dt = None
|
||||||
|
|
||||||
for pattern in date_patterns:
|
for pattern in date_patterns:
|
||||||
match = re.search(pattern, message.text, re.IGNORECASE)
|
match = re.search(pattern, msg_text, re.IGNORECASE)
|
||||||
if match:
|
if match:
|
||||||
date_found_dt = translate_date_to_datetime(match.group())
|
date_found_dt = translate_date_to_datetime(match.group())
|
||||||
|
|
||||||
@@ -310,10 +399,15 @@ def text_message(message: types.Message):
|
|||||||
|
|
||||||
db.add_note(
|
db.add_note(
|
||||||
message.chat.id,
|
message.chat.id,
|
||||||
message.text,
|
msg_text,
|
||||||
date_found_dt,
|
date_found_dt,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
reaction = types.ReactionTypeEmoji(random.choice(list_reactions))
|
||||||
|
|
||||||
|
bot.set_message_reaction(
|
||||||
|
message.chat.id, message.message_id, [reaction], is_big=True
|
||||||
|
)
|
||||||
send_message(message, "📝 Напоминание добавлено:")
|
send_message(message, "📝 Напоминание добавлено:")
|
||||||
list_notes(message)
|
list_notes(message)
|
||||||
|
|
||||||
@@ -328,6 +422,8 @@ def send_message(
|
|||||||
if not thread_id:
|
if not thread_id:
|
||||||
thread_id = message.message_thread_id
|
thread_id = message.message_thread_id
|
||||||
|
|
||||||
|
msg = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
msg = bot.send_message(
|
msg = bot.send_message(
|
||||||
chat_id=id,
|
chat_id=id,
|
||||||
@@ -371,13 +467,13 @@ if __name__ == "__main__":
|
|||||||
chat_id, list, ind_note = db.check_old_notes()
|
chat_id, list, ind_note = db.check_old_notes()
|
||||||
|
|
||||||
if chat_id:
|
if chat_id:
|
||||||
note = db.get_notes(chat_id, list)["notes"][ind_note]
|
note = db.get_notes(chat_id, list)[ind_note]
|
||||||
|
|
||||||
db.delete_note(chat_id, ind_note, list)
|
db.delete_note(chat_id, ind_note, list)
|
||||||
|
|
||||||
msg = bot.send_message(
|
msg = bot.send_message(
|
||||||
chat_id,
|
chat_id,
|
||||||
f"❌ {note['text']} \n(Уведомление: {datetime.strptime(note['time_notif'], '%Y-%m-%d %H:%M:%S').strftime('%d %B %Y')})",
|
f"❌ {note['content']} \n(Уведомление: {note['remind_at'].strftime('%d %B %Y')})",
|
||||||
timeout=1,
|
timeout=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user