mirror of
https://gitlab.com/questable/questable_bot
synced 2025-07-12 06:56:53 +02:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
6116594eb4 | |||
1f0a05ca9d | |||
d2b70dacd8 | |||
c5b7eba052 | |||
1cc0743061 | |||
514117a079 | |||
ac3e09fb19 | |||
3604248a83 | |||
0ab2fa5b6b | |||
0a41d3d868 |
29
README.md
29
README.md
@ -1,3 +1,30 @@
|
|||||||
# Questable
|
# Questable
|
||||||
|
|
||||||
A game-like To-Do List Telegram Bot
|
A game-like To-Do List Telegram Bot.
|
||||||
|
|
||||||
|
Source code for [Questable Bot](https://t.me/questable_bot) and the relevant
|
||||||
|
[API](https://api.questable.webionite.com/)
|
||||||
|
|
||||||
|
# Self Hosting
|
||||||
|
|
||||||
|
+ Clone the repository.
|
||||||
|
+ `git clone https://gitlab.com/questable/questable_bot.git`
|
||||||
|
+ `cd questable`
|
||||||
|
|
||||||
|
## Telegram Bot
|
||||||
|
|
||||||
|
+ Install the dependencies
|
||||||
|
+ `pip3 install python-telegram-bot`
|
||||||
|
+ Copy `sample.config.py` to `config.py` and edit it accordingly.
|
||||||
|
+ Run the bot
|
||||||
|
+ `python3 bot.py`
|
||||||
|
|
||||||
|
## Questable API Server
|
||||||
|
|
||||||
|
+ Install the dependencies
|
||||||
|
+ `pip3 install Flask flask_cors`
|
||||||
|
+ Install `gunicorn`
|
||||||
|
+ `pip3 install gunicorn`
|
||||||
|
+ Run `gunicorn3 -b 127.0.0.1:5000 server:app`. Change port if you want to run
|
||||||
|
gunicorn on a different port.
|
||||||
|
+ Set up a reverse proxy from your webserver to `localhost:5000`.
|
||||||
|
83
bot.py
83
bot.py
@ -98,11 +98,11 @@ def add_name(bot, update, player, type, qid):
|
|||||||
def add_diff(bot, update, player, type, qid):
|
def add_diff(bot, update, player, type, qid):
|
||||||
message = update.message.text.lower()
|
message = update.message.text.lower()
|
||||||
chat_id = update.message.chat_id
|
chat_id = update.message.chat_id
|
||||||
if message == "low" or message == "📙 low":
|
if message in ["low", "📙 low", "l"]:
|
||||||
diff = 1
|
diff = 1
|
||||||
elif message == "medium" or message == "📘 medium":
|
elif message in ["medium", "📘 medium", "m"]:
|
||||||
diff = 2
|
diff = 2
|
||||||
elif message == "high" or message == "📗 high":
|
elif message in ["high", "📗 high", "h"]:
|
||||||
diff = 3
|
diff = 3
|
||||||
else:
|
else:
|
||||||
bot.send_message(chat_id=chat_id, text="Invalid Option")
|
bot.send_message(chat_id=chat_id, text="Invalid Option")
|
||||||
@ -129,11 +129,11 @@ def add_diff(bot, update, player, type, qid):
|
|||||||
def add_imp(bot, update, player, type, qid):
|
def add_imp(bot, update, player, type, qid):
|
||||||
message = update.message.text.lower()
|
message = update.message.text.lower()
|
||||||
chat_id = update.message.chat_id
|
chat_id = update.message.chat_id
|
||||||
if message == "low" or message == "🔹 low":
|
if message in ["low", "🔹 low", "l"]:
|
||||||
imp = 1
|
imp = 1
|
||||||
elif message == "medium" or message == "🔸 medium":
|
elif message in ["medium", "🔸 medium", "m"]:
|
||||||
imp = 2
|
imp = 2
|
||||||
elif message == "high" or message == "🔺 high":
|
elif message in ["high", "🔺 high", "h"]:
|
||||||
imp = 3
|
imp = 3
|
||||||
else:
|
else:
|
||||||
bot.send_message(chat_id=chat_id, text="Invalid Option")
|
bot.send_message(chat_id=chat_id, text="Invalid Option")
|
||||||
@ -305,11 +305,11 @@ def edit_quest(bot, update, player, qid, target, type):
|
|||||||
text = "<b>☑️ Updated Name</b>"
|
text = "<b>☑️ Updated Name</b>"
|
||||||
elif target == "imp":
|
elif target == "imp":
|
||||||
message = message.lower()
|
message = message.lower()
|
||||||
if message == "low" or message == "🔹 low":
|
if message in ["low", "🔹 low", "l"]:
|
||||||
x.imp = 1
|
x.imp = 1
|
||||||
elif message == "medium" or message == "🔸 medium":
|
elif message in ["medium", "🔸 medium", "m"]:
|
||||||
x.imp = 2
|
x.imp = 2
|
||||||
elif message == "high" or message == "🔺 high":
|
elif message in ["high", "🔺 high", "h"]:
|
||||||
x.imp = 3
|
x.imp = 3
|
||||||
else:
|
else:
|
||||||
bot.send_message(chat_id=chat_id, text="Invalid Option")
|
bot.send_message(chat_id=chat_id, text="Invalid Option")
|
||||||
@ -317,11 +317,11 @@ def edit_quest(bot, update, player, qid, target, type):
|
|||||||
text = "<b>☑️ Updated Priority</b>"
|
text = "<b>☑️ Updated Priority</b>"
|
||||||
elif target == "diff":
|
elif target == "diff":
|
||||||
message = message.lower()
|
message = message.lower()
|
||||||
if message == "low" or message == "📙 low":
|
if message in ["low", "📙 low", "l"]:
|
||||||
x.diff = 1
|
x.diff = 1
|
||||||
elif message == "medium" or message == "📘 medium":
|
elif message in ["medium", "📘 medium", "m"]:
|
||||||
x.diff = 2
|
x.diff = 2
|
||||||
elif message == "high" or message == "📗 high":
|
elif message in ["high", "📗 high", "h"]:
|
||||||
x.diff = 3
|
x.diff = 3
|
||||||
else:
|
else:
|
||||||
bot.send_message(chat_id=chat_id, text="Invalid Option")
|
bot.send_message(chat_id=chat_id, text="Invalid Option")
|
||||||
@ -401,9 +401,13 @@ def tokens(bot, update):
|
|||||||
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
||||||
reply_text = ("Tokens are used to authenticate external "
|
reply_text = ("Tokens are used to authenticate external "
|
||||||
"applications. This only provides access to "
|
"applications. This only provides access to "
|
||||||
"Questable data.")
|
"Questable data.\n"
|
||||||
|
"\nOfficial clients are:\n"
|
||||||
|
"[Questable CLI](https://gitlab.com/questable/questable-cli)"
|
||||||
|
)
|
||||||
bot.send_message(chat_id=update.message.chat_id, text=reply_text,
|
bot.send_message(chat_id=update.message.chat_id, text=reply_text,
|
||||||
reply_markup=reply_markup)
|
reply_markup=reply_markup, parse_mode="markdown",
|
||||||
|
disable_web_page_preview=True)
|
||||||
|
|
||||||
|
|
||||||
def add_token(bot, update, player):
|
def add_token(bot, update, player):
|
||||||
@ -466,24 +470,25 @@ def message_handling(bot, update, db):
|
|||||||
# rt: Remove token
|
# rt: Remove token
|
||||||
|
|
||||||
if state["state"] == "none":
|
if state["state"] == "none":
|
||||||
if text == "add quest" or text == "❇️ add quest":
|
if text in ["add quest", "❇️ add quest", "aq"]:
|
||||||
add_quest(bot, update, player)
|
add_quest(bot, update, player)
|
||||||
elif text == "add side quest" or text == "📯 add side quest":
|
elif text in ["add side quest", "📯 add side quest", "asq"]:
|
||||||
add_quest(bot, update, player, "side_quest")
|
add_quest(bot, update, player, "side_quest")
|
||||||
elif text == "list quests" or text == "📜 list quests":
|
elif text in ["list quests", "📜 list quests", "lq"]:
|
||||||
list_quests(bot, update, player, "quest")
|
list_quests(bot, update, player, "quest")
|
||||||
elif text == "list side quests" or text == "📃 list side quests":
|
elif text in ["list side quests", "📃 list side quests", "lsq"]:
|
||||||
list_quests(bot, update, player, "side_quest")
|
list_quests(bot, update, player, "side_quest")
|
||||||
elif text == "tokens" or text == "🔑 tokens":
|
elif text in ["tokens", "🔑 tokens", "t"]:
|
||||||
tokens(bot, update)
|
tokens(bot, update)
|
||||||
elif text == "list tokens" or text == "📋 list tokens":
|
elif text in ["list tokens", "📋 list tokens", "lt"]:
|
||||||
list_tokens(bot, update, player)
|
list_tokens(bot, update, player)
|
||||||
|
elif text in ["generate token", "🔑 generate token", "gt"]:
|
||||||
elif text == "generate token" or text == "🔑 generate token":
|
|
||||||
add_token(bot, update, player)
|
add_token(bot, update, player)
|
||||||
|
elif text in ["delete token", "🧹 delete token", "dt"]:
|
||||||
elif text == "delete token" or text == "🧹 delete token":
|
|
||||||
delete_token(bot, update, player)
|
delete_token(bot, update, player)
|
||||||
|
elif text == "ls":
|
||||||
|
list_quests(bot, update, player, "side_quest")
|
||||||
|
list_quests(bot, update, player, "quest")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if update.message.chat.type == "private":
|
if update.message.chat.type == "private":
|
||||||
@ -508,32 +513,32 @@ def message_handling(bot, update, db):
|
|||||||
add_imp(bot, update, player, "side_quest", state["extra"])
|
add_imp(bot, update, player, "side_quest", state["extra"])
|
||||||
|
|
||||||
elif state["state"] == "eq":
|
elif state["state"] == "eq":
|
||||||
if text == "back" or text == "⬅️ back":
|
if text in ["back", "⬅️ back", "b"]:
|
||||||
player.set_state('none', 0)
|
player.set_state('none', 0)
|
||||||
send_status(bot, update, player)
|
send_status(bot, update, player)
|
||||||
elif text == "mark as done" or text == "✅ mark as done":
|
elif text in ["mark as done", "✅ mark as done", "mad"]:
|
||||||
mark_as_done(bot, update, player, state["extra"], "quest")
|
mark_as_done(bot, update, player, state["extra"], "quest")
|
||||||
elif text == "edit name" or text == "📝 edit name":
|
elif text in ["edit name", "📝 edit name", "en"]:
|
||||||
player.set_state('eqn', state["extra"])
|
player.set_state('eqn', state["extra"])
|
||||||
text = "What shall the new name of the Quest be?"
|
text = "What shall the new name of the Quest be?"
|
||||||
reply_markup = telegram.ReplyKeyboardRemove()
|
reply_markup = telegram.ReplyKeyboardRemove()
|
||||||
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
||||||
reply_markup=reply_markup)
|
reply_markup=reply_markup)
|
||||||
elif text == "change priority" or text == "⚠️ change priority":
|
elif text in ["change priority", "⚠️ change priority", "cp"]:
|
||||||
player.set_state('eqi', state["extra"])
|
player.set_state('eqi', state["extra"])
|
||||||
text = "How important is it?"
|
text = "How important is it?"
|
||||||
custom_keyboard = button_groups.importance
|
custom_keyboard = button_groups.importance
|
||||||
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
||||||
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
||||||
reply_markup=reply_markup)
|
reply_markup=reply_markup)
|
||||||
elif text == "change difficulty" or text == "📚 change difficulty":
|
elif text in ["change difficulty", "📚 change difficulty", "cd"]:
|
||||||
player.set_state('eqd', state["extra"])
|
player.set_state('eqd', state["extra"])
|
||||||
text = "How difficult is it?"
|
text = "How difficult is it?"
|
||||||
custom_keyboard = button_groups.difficulty
|
custom_keyboard = button_groups.difficulty
|
||||||
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
||||||
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
||||||
reply_markup=reply_markup)
|
reply_markup=reply_markup)
|
||||||
elif text == "delete quest" or text == "🗑 delete quest":
|
elif text in ["delete quest", "🗑 delete quest", "dq"]:
|
||||||
quest = questable.get_quest(db, player.CHAT_ID, state["extra"])
|
quest = questable.get_quest(db, player.CHAT_ID, state["extra"])
|
||||||
quest.delete_from_db()
|
quest.delete_from_db()
|
||||||
drop_state(bot, update, player)
|
drop_state(bot, update, player)
|
||||||
@ -545,32 +550,32 @@ def message_handling(bot, update, db):
|
|||||||
send_status(bot, update, player)
|
send_status(bot, update, player)
|
||||||
|
|
||||||
elif state["state"] == "esq":
|
elif state["state"] == "esq":
|
||||||
if text == "back" or text == "⬅️ back":
|
if text in ["back", "⬅️ back", "b"]:
|
||||||
player.set_state('none', 0)
|
player.set_state('none', 0)
|
||||||
send_status(bot, update, player)
|
send_status(bot, update, player)
|
||||||
elif text == "mark as done" or text == "✅ mark as done":
|
elif text in ["mark as done", "✅ mark as done", "mad"]:
|
||||||
mark_as_done(bot, update, player, state["extra"], "side_quest")
|
mark_as_done(bot, update, player, state["extra"], "side_quest")
|
||||||
elif text == "edit name" or text == "📝 edit name":
|
elif text in ["edit name", "📝 edit name", "en"]:
|
||||||
player.set_state('esqn', state["extra"])
|
player.set_state('esqn', state["extra"])
|
||||||
text = "What shall the new name of the Side Quest be?"
|
text = "What shall the new name of the Side Quest be?"
|
||||||
reply_markup = telegram.ReplyKeyboardRemove()
|
reply_markup = telegram.ReplyKeyboardRemove()
|
||||||
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
||||||
reply_markup=reply_markup)
|
reply_markup=reply_markup)
|
||||||
elif text == "change priority" or text == "⚠️ change priority":
|
elif text in ["change priority", "⚠️ change priority", "cp"]:
|
||||||
player.set_state('esqi', state["extra"])
|
player.set_state('esqi', state["extra"])
|
||||||
text = "How important is it?"
|
text = "How important is it?"
|
||||||
custom_keyboard = button_groups.importance
|
custom_keyboard = button_groups.importance
|
||||||
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
||||||
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
||||||
reply_markup=reply_markup)
|
reply_markup=reply_markup)
|
||||||
elif text == "change difficulty" or text == "📚 change difficulty":
|
elif text in ["change difficulty", "📚 change difficulty", "cd"]:
|
||||||
player.set_state('esqd', state["extra"])
|
player.set_state('esqd', state["extra"])
|
||||||
text = "How difficult is it?"
|
text = "How difficult is it?"
|
||||||
custom_keyboard = button_groups.difficulty
|
custom_keyboard = button_groups.difficulty
|
||||||
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
|
||||||
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
bot.send_message(chat_id=player.CHAT_ID, text=text,
|
||||||
reply_markup=reply_markup)
|
reply_markup=reply_markup)
|
||||||
elif text == "delete side quest" or text == "🗑 delete side quest":
|
elif text in ["delete side quest", "🗑 delete side quest", "dsq"]:
|
||||||
sq = questable.get_side_quest(db, player.CHAT_ID, state["extra"])
|
sq = questable.get_side_quest(db, player.CHAT_ID, state["extra"])
|
||||||
sq.delete_from_db()
|
sq.delete_from_db()
|
||||||
drop_state(bot, update, player)
|
drop_state(bot, update, player)
|
||||||
@ -626,7 +631,7 @@ with open('schema.sql') as f:
|
|||||||
cursor.executescript(f.read())
|
cursor.executescript(f.read())
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
updater = Updater(token=config.api_key)
|
updater = Updater(token=config.api_key, use_context=False)
|
||||||
dispatcher = updater.dispatcher
|
dispatcher = updater.dispatcher
|
||||||
|
|
||||||
dispatcher.add_handler(CommandHandler('start', start))
|
dispatcher.add_handler(CommandHandler('start', start))
|
||||||
@ -637,12 +642,12 @@ dispatcher.add_handler(CommandHandler('cancel', lambda x, y: me_handler(x, y,
|
|||||||
db)))
|
db)))
|
||||||
dispatcher.add_handler(CommandHandler('help', lambda x, y: help_command(x, y,
|
dispatcher.add_handler(CommandHandler('help', lambda x, y: help_command(x, y,
|
||||||
db)))
|
db)))
|
||||||
dispatcher.add_handler(MessageHandler(Filters.text, lambda x, y:
|
|
||||||
message_handling(x, y, db)))
|
|
||||||
dispatcher.add_handler(RegexHandler(r"/[Ss]?[Qq]_\d+", lambda x, y:
|
dispatcher.add_handler(RegexHandler(r"/[Ss]?[Qq]_\d+", lambda x, y:
|
||||||
quest_handling(x, y, db)))
|
quest_handling(x, y, db)))
|
||||||
dispatcher.add_handler(MessageHandler(Filters.command, lambda x, y:
|
dispatcher.add_handler(MessageHandler(Filters.command, lambda x, y:
|
||||||
message_handling(x, y, db)))
|
message_handling(x, y, db)))
|
||||||
|
dispatcher.add_handler(MessageHandler(Filters.text, lambda x, y:
|
||||||
|
message_handling(x, y, db)))
|
||||||
|
|
||||||
if config.update_method == "polling":
|
if config.update_method == "polling":
|
||||||
updater.start_polling()
|
updater.start_polling()
|
||||||
|
@ -9,7 +9,7 @@ class base_quest():
|
|||||||
self.DB = db
|
self.DB = db
|
||||||
self.CHAT_ID = chat_id
|
self.CHAT_ID = chat_id
|
||||||
self.name = name
|
self.name = name
|
||||||
self.QID = qid
|
self.QID = int(qid)
|
||||||
self.imp = imp
|
self.imp = imp
|
||||||
self.diff = diff
|
self.diff = diff
|
||||||
self.state = state
|
self.state = state
|
||||||
|
44
schema.sql
44
schema.sql
@ -1,5 +1,39 @@
|
|||||||
CREATE TABLE IF NOT EXISTS quests(chat_id int NOT NULL, qid int NOT NULL, name varchar(255), difficulty int, importance int, state int DEFAULT 0, UNIQUE(chat_id, qid));
|
CREATE TABLE IF NOT EXISTS quests(
|
||||||
CREATE TABLE IF NOT EXISTS side_quests(chat_id int NOT NULL, qid int NOT NULL, name varchar(255), difficulty int, importance int, state int DEFAULT 0, UNIQUE(chat_id, qid));
|
chat_id int NOT NULL,
|
||||||
CREATE TABLE IF NOT EXISTS points(chat_id int PRIMARY KEY, points int);
|
qid int NOT NULL,
|
||||||
CREATE TABLE IF NOT EXISTS state(chat_id int PRIMARY KEY, state varchar(10), extra varchar(10));
|
name varchar(255),
|
||||||
CREATE TABLE IF NOT EXISTS tokens(chat_id int, token varchar(36) PRIMARY KEY);
|
difficulty int,
|
||||||
|
importance int,
|
||||||
|
state int DEFAULT 0,
|
||||||
|
UNIQUE(chat_id, qid)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS side_quests(
|
||||||
|
chat_id int NOT NULL,
|
||||||
|
qid int NOT NULL,
|
||||||
|
name varchar(255),
|
||||||
|
difficulty int,
|
||||||
|
importance int,
|
||||||
|
state int DEFAULT 0,
|
||||||
|
UNIQUE(chat_id, qid)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS points(
|
||||||
|
chat_id int PRIMARY KEY,
|
||||||
|
points int
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS state(
|
||||||
|
chat_id int PRIMARY KEY,
|
||||||
|
state varchar(10),
|
||||||
|
extra varchar(10)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS tokens(
|
||||||
|
chat_id int,
|
||||||
|
token varchar(36) PRIMARY KEY
|
||||||
|
);
|
||||||
|
@ -4,8 +4,10 @@ import questable
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import errors
|
import errors
|
||||||
from flask import Flask, jsonify, request, redirect
|
from flask import Flask, jsonify, request, redirect
|
||||||
|
from flask_cors import CORS
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
CORS(app)
|
||||||
db = sqlite3.connect("questable.db", check_same_thread=False)
|
db = sqlite3.connect("questable.db", check_same_thread=False)
|
||||||
|
|
||||||
|
|
||||||
@ -235,7 +237,8 @@ def update_quest(db):
|
|||||||
else:
|
else:
|
||||||
return jsonify(errors._400_bv), 400
|
return jsonify(errors._400_bv), 400
|
||||||
elif i == "state":
|
elif i == "state":
|
||||||
state = bool(request.values["state"])
|
state = (True if str(request.values['state']).lower()
|
||||||
|
in ["1", "true"] else False)
|
||||||
if state is True:
|
if state is True:
|
||||||
quest.state = 1
|
quest.state = 1
|
||||||
else:
|
else:
|
||||||
@ -294,7 +297,8 @@ def update_side_quest(db):
|
|||||||
else:
|
else:
|
||||||
return jsonify(errors._400_bv), 400
|
return jsonify(errors._400_bv), 400
|
||||||
elif i == "state":
|
elif i == "state":
|
||||||
state = bool(request.values["state"])
|
state = (True if str(request.values['state']).lower()
|
||||||
|
in ["1", "true"] else False)
|
||||||
if state is True:
|
if state is True:
|
||||||
quest.state = 1
|
quest.state = 1
|
||||||
else:
|
else:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user