aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorrahiel <rahiel@protonmail.ch>2015-09-22 13:25:32 +0200
committerrahiel <rahiel@protonmail.ch>2015-09-22 13:25:32 +0200
commit5d1a1f90834c9e52b1c1c951f6ce28916354bbe8 (patch)
treedb711463c02cd4327e8729fb95ca2dd7bb5bd88d
Bismillah
-rw-r--r--.gitignore2
-rw-r--r--bismillah.py168
-rwxr-xr-xquran.py128
3 files changed, 298 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96fff66
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+secret.py
diff --git a/bismillah.py b/bismillah.py
new file mode 100644
index 0000000..39ee410
--- /dev/null
+++ b/bismillah.py
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+import re
+from time import sleep
+
+import telegram
+from redis import StrictRedis
+import ujson as json
+
+from quran import Quran
+from secret import TOKEN
+
+
+english = Quran("translation")
+tafsir = Quran("tafsir")
+r = StrictRedis()
+redis_namespace = ""
+
+
+def save_user(chat_id, state):
+ """State is a list: [surah:int, ayah:int, type:str]"""
+ r.set(redis_namespace + str(chat_id),
+ json.dumps(state), ex=31536000) # keep state for a year
+
+
+def get_user(chat_id):
+ v = r.get(redis_namespace + str(chat_id))
+ if v is not None:
+ return json.loads(v)
+
+
+def save_file(filename, file_id):
+ r.set(redis_namespace + "file:" + filename,
+ json.dumps(file_id), ex=8035200) # keep for 3 months
+
+
+def get_file(filename):
+ f = r.get(redis_namespace + "file:" + filename)
+ if f is not None:
+ return json.loads(f)
+
+
+def main():
+ bot = telegram.Bot(token=TOKEN)
+ update_id = None
+
+ data = {"english": english, "tafsir": tafsir}
+
+ while True:
+ try:
+ update_id = serve(bot, update_id, data)
+ except ValueError as e:
+ # ValueError: No JSON object could be decoded
+ sleep(3)
+ except telegram.TelegramError as e:
+ if "HTTP Error 502" in e.message:
+ # telegram.error.TelegramError: HTTP Error 502: Bad Gateway
+ sleep(5)
+ else:
+ raise e
+
+
+def serve(bot, update_id, data):
+ interface = telegram.ReplyKeyboardMarkup(
+ [["Arabic", "Audio", "English", "Tafsir"],
+ ["Previous", "Random", "Next"]],
+ resize_keyboard=True)
+
+ def send_quran(s, a, quran_type, chat_id, reply_markup=None):
+ if not (0 < s < 115 and 0 < a <= Quran.surah_lengths[s]):
+ bot.sendMessage(chat_id=chat_id, text="Ayah does not exist!")
+ return
+ elif quran_type in ("english", "tafsir"):
+ text = data[quran_type].getAyah(s, a)
+ bot.sendMessage(chat_id=chat_id, text=text, reply_markup=reply_markup)
+ elif quran_type == "arabic":
+ bot.sendChatAction(chat_id=chat_id,
+ action=telegram.ChatAction.UPLOAD_PHOTO)
+ image = "quran_images/" + str(s) + '_' + str(a) + ".png"
+ upload_file(image, quran_type, chat_id=chat_id,
+ caption="Quran %d:%d" % (s, a),
+ reply_markup=reply_markup)
+ elif quran_type == "audio":
+ bot.sendChatAction(chat_id=chat_id,
+ action=telegram.ChatAction.UPLOAD_AUDIO)
+ audio = ("Husary/" + str(s).zfill(3) + str(a).zfill(3) + ".mp3")
+ upload_file(audio, quran_type, chat_id=chat_id,
+ performer="Shaykh Mahmoud Khalil al-Husary",
+ title="Quran %d:%d" % (s, a),
+ reply_markup=reply_markup)
+ save_user(chat_id, [s, a, quran_type])
+
+ def upload_file(filename, quran_type, **kwargs):
+
+ def upload(f):
+ if quran_type == "arabic":
+ v = bot.sendPhoto(photo=f, **kwargs)["photo"][-1]["file_id"]
+ elif quran_type == "audio":
+ v = bot.sendAudio(audio=f, **kwargs)["audio"]["file_id"]
+ save_file(filename, v)
+
+ def upload_from_disk():
+ with open(filename, "rb") as f:
+ upload(f)
+
+ f = get_file(filename)
+ if f is not None:
+ try:
+ upload(f)
+ except telegram.TelegramError as e:
+ if "file_id" in e.message:
+ upload_from_disk()
+ else:
+ raise e
+ else:
+ upload_from_disk()
+
+ for update in bot.getUpdates(offset=update_id, timeout=10):
+ chat_id = update.message.chat_id
+ update_id = update.update_id + 1 # put in redis?
+ message = update.message.text.encode("utf-8").lower()
+ state = get_user(chat_id)
+ if state is not None:
+ s, a, quran_type = state
+ else:
+ s, a, quran_type = 1, 1, "english"
+
+ print update
+
+ if chat_id < 0:
+ continue # bot should not be in a group
+
+ if message.startswith('/'):
+ command = message[1:]
+ if command in ("start", "help"):
+ text = ("Send me the numbers of a surah and ayah, for example"
+ " '2 255' and I respond with that ayah from the Noble "
+ "Quran. Or type: random.")
+ elif command == "about":
+ text = ("The English translation is by Imam Ahmed Raza from "
+ "tanzil.net/trans/. The audio is a recitation by "
+ "Shaykh Mahmoud Khalil al-Husary from everyayah.com. "
+ "The tafsir is Tafsir al-Jalalayn from altafsir.com")
+ else:
+ text = "Invalid command"
+ bot.sendMessage(chat_id=chat_id, text=text)
+ continue
+
+ match = re.match("(\d+)[ :]*(\d*)", message)
+ if match is not None:
+ s = int(match.group(1))
+ a = int(match.group(2)) if match.group(2) else 1
+ send_quran(s, a, quran_type, chat_id, reply_markup=interface)
+ elif message in ("english", "tafsir", "audio", "arabic"):
+ send_quran(s, a, message, chat_id)
+ elif message in ("next", "previous", "random"):
+ if message == "next":
+ s, a = Quran.getNextAyah(s, a)
+ elif message == "previous":
+ s, a = Quran.getPreviousAyah(s, a)
+ elif message == "random":
+ s, a = Quran.getRandomAyah()
+ send_quran(s, a, quran_type, chat_id)
+
+ return update_id
+
+
+if __name__ == '__main__':
+ main()
diff --git a/quran.py b/quran.py
new file mode 100755
index 0000000..03eb52d
--- /dev/null
+++ b/quran.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+import re
+from random import randint
+
+
+def parse_quran_trans():
+ quran = []
+ surah = []
+ s = 1
+
+ def process_verse(verse):
+ """Add verse and replace for Arabic ligatures (salawat)"""
+ return (verse.strip()
+ .replace("– peace and blessings be upon him", 'ﷺ‎'))
+
+ with open("en.ahmedraza", 'r') as f:
+ for line in f.readlines():
+ if line == '\n': continue
+ if line[:3] == "#==": break
+ verse = line.split('|')
+ assert len(verse) == 3
+ if int(verse[0]) == s:
+ surah.append(process_verse(verse[2]))
+ else:
+ quran.append(surah)
+ surah = [process_verse(verse[2])]
+ s += 1
+ quran.append(surah)
+ assert sum([len(s) for s in quran]) == 6236, "Missing verses!"
+ return quran
+
+
+def parse_quran_tafsir():
+ quran = []
+ surah = []
+ s, v = 1, 1
+ in_verse = False
+ verse = []
+
+ def add_verse(verse, surah):
+ surah.append(' '.join(verse))
+
+ def add_line(line, verse):
+ """Add line and replace for Arabic ligatures (salawat)"""
+ verse.append(line.strip().replace("(s)", 'ﷺ‎'))
+
+ with open("Al_Jalalain_Eng.txt", 'r') as f:
+ for line in f.readlines():
+ if line == '\n': continue
+ if re.match("\d+\w*", line): continue
+ elif line.startswith("[%d:%d]" % (s, v)):
+ in_verse = True
+ elif line.startswith("[%d:%d]" % (s, v + 1)):
+ add_verse(verse, surah)
+ verse = []
+ v += 1
+ in_verse = True
+ elif line.startswith("[%d:1]" % (s + 1)):
+ add_verse(verse, surah)
+ verse = []
+ quran.append(surah)
+ surah = []
+ s += 1
+ v = 1
+ in_verse = True
+ elif (line.startswith("Medinese") or line.startswith("Meccan") or
+ line.startswith("[Consists") or # [Consists end surah 5
+ line.startswith("Mecca, consisting") or # end surah 73
+ line.startswith("This was revealed")):
+ if s == 26 and v == 200:
+ # only line starting with "Meccan" that is part of a verse
+ add_line(line, verse)
+ else:
+ in_verse = False
+ elif in_verse:
+ add_line(line, verse)
+ add_verse(verse, surah)
+ quran.append(surah)
+ assert sum([len(s) for s in quran]) == 6236, "Missing verses!"
+ return quran
+
+
+class Quran(object):
+ surah_lengths = [0, 7, 286, 200, 176, 120, 165, 206, 75, 129, 109, 123, 111, 43, 52, 99, 128, 111, 110, 98, 135, 112, 78, 118, 64, 77, 227, 93, 88, 69, 60, 34, 30, 73, 54, 45, 83, 182, 88, 75, 85, 54, 53, 89, 59, 37, 35, 38, 29, 18, 45, 60, 49, 62, 55, 78, 96, 29, 22, 24, 13, 14, 11, 11, 18, 12, 12, 30, 52, 52, 44, 28, 28, 20, 56, 40, 31, 50, 40, 46, 42, 29, 19, 36, 25, 22, 17, 19, 26, 30, 20, 15, 21, 11, 8, 8, 19, 5, 8, 8, 11, 11, 8, 3, 9, 5, 4, 7, 3, 6, 3, 5, 4, 5, 6]
+ # [0] for normal numbering
+
+ def __init__(self, data):
+ if data == "translation":
+ self.text = parse_quran_trans()
+ elif data == "tafsir":
+ self.text = parse_quran_tafsir()
+
+ def getSurah(self, surah):
+ """Get surah by number."""
+ return self.text[surah - 1]
+
+ def getAyah(self, surah, ayah):
+ """Get verse by surah and ayah numbers."""
+ return self.text[surah - 1][ayah - 1] + " (%d:%d)" % (surah, ayah)
+
+ def getAyahs(self, surah, a, b):
+ """Get range of Ayahs as tuple """
+ return ' '.join(self.text[surah - 1][a - 1:b]) + " (%d:%d-%d)" % (surah, a, b)
+
+ @classmethod
+ def getRandomAyah(cls):
+ surah = randint(1, 114)
+ ayah = randint(1, Quran.surah_lengths[surah])
+ return surah, ayah
+
+ @classmethod
+ def getNextAyah(cls, s, a):
+ length = Quran.surah_lengths[s]
+ if a == length:
+ s = s + 1 if s < 114 else 1
+ a = 1
+ else:
+ a += 1
+ return s, a
+
+ @classmethod
+ def getPreviousAyah(cls, s, a):
+ if a == 1:
+ s = s - 1 if s > 1 else 114
+ a = 1
+ else:
+ a -= 1
+ return s, a