Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 26 additions & 26 deletions src/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,11 @@ def start(update, context):
"""
Greets user on chat startup
"""
update.message.reply_text(text='Hello and welcome')
update.message.reply_text(text="Hello and welcome")


def help_info(update, context):
update.message.reply_text(
text=HELP_INFO,
parse_mode=PARSEMODE_HTML
)
update.message.reply_text(text=HELP_INFO, parse_mode=PARSEMODE_HTML)


def analyze_history(update, context):
Expand All @@ -34,20 +31,20 @@ def analyze_history(update, context):
chat_json = _unpack_telegram_document(update)
messages_df = _form_data_frame_from_json(chat_json)
if messages_df is None:
update.message.reply_text('Exit early. Invalid json')
update.message.reply_text("Exit early. Invalid json")
return
photos = make_plots(messages_df)
update.message.reply_media_group(media=photos)


def start_shipping_callback(update: Update, context: CallbackContext):
update.message.reply_invoice(
title='Some gifts',
description='We need your address',
payload='UKASSA',
provider_token=os.getenv('UKASSA_TEST_TOKEN'),
currency='RUB',
prices=[LabeledPrice('Test', 100*100)],
title="Some gifts",
description="We need your address",
payload="UKASSA",
provider_token=os.getenv("UKASSA_TEST_TOKEN"),
currency="RUB",
prices=[LabeledPrice("Test", 100 * 100)],
need_phone_number=True,
need_email=True,
need_shipping_address=True,
Expand All @@ -57,35 +54,38 @@ def start_shipping_callback(update: Update, context: CallbackContext):

def start_noshipping_callback(update: Update, context: CallbackContext):
update.message.reply_invoice(
title='Premium status',
description='One month of premium access',
payload='UKASSA',
provider_token=os.getenv('UKASSA_TEST_TOKEN'),
currency='RUB',
prices=[LabeledPrice('Test', 100*100)],
title="Premium status",
description="One month of premium access",
payload="UKASSA",
provider_token=os.getenv("UKASSA_TEST_TOKEN"),
currency="RUB",
prices=[LabeledPrice("Test", 100 * 100)],
need_email=True,
)


def shipping_callback(update: Update, context: CallbackContext) -> None:
query = update.shipping_query
if query.invoice_payload != 'UKASSA':
query.answer(ok=False, error_message='Something went wrong')
if query.invoice_payload != "UKASSA":
query.answer(ok=False, error_message="Something went wrong")
return

options = [ShippingOption('1', 'Option A', [LabeledPrice('A', 100)])]
price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)]
options.append(ShippingOption('2', 'Option B', price_list))
options = [ShippingOption("1", "Option A", [LabeledPrice("A", 100)])]
price_list = [LabeledPrice("B1", 150), LabeledPrice("B2", 200)]
options.append(ShippingOption("2", "Option B", price_list))

query.answer(ok=True, shipping_options=options)


def pre_checkout_callback(update: Update, context: CallbackContext) -> None:
query = update.pre_checkout_query
params = dict(ok=True) if query.invoice_payload == 'UKASSA'\
else dict(ok=False, error_message='Something went wrong!')
params = (
dict(ok=True)
if query.invoice_payload == "UKASSA"
else dict(ok=False, error_message="Something went wrong!")
)
query.answer(**params)


def successful_payment(update: Update, context: CallbackContext) -> None:
update.message.reply_text('Thank you for your payment')
update.message.reply_text("Thank you for your payment")
46 changes: 24 additions & 22 deletions src/doc.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
HELP_INFO = '1. Exporting history.' \
'\nIn Telegram you can easily export your chat history. ' \
'I can only work with personal chats for now, so please pick one ' \
'and let\'s analyze it! ' \
'Here is an <a href="https://telegram.org/blog/export-and-more">official guide</a> on how to ' \
'get a file with your history. ' \
'Please note, this feature available only from Desktop App. Remember ' \
'to save in .json format, not in .html.' \
'\n\n2. How it works.' \
'\nAfter you done with exporting desired history - ' \
'we ready to go. Just attach "result.json" file to a regular message and send ' \
'it to me. I\'ll try to do my best analyzing it for you and send back some plots.' \
'My code is open and I do not collect any parts of your data, ' \
'but if you worry about it you can consider next step.' \
'\n\n3. You should feel free to delete essential ' \
'parts of your messaging history (like messages <i>text, user id,</i> etc) before sending it to me' \
'\nI\'ll try to stay functional. ' \
'But there is recommended set of messages params ' \
'to be present in your json: <i>id, type, date, from, media_type</i>. ' \
'Here is some resources were you can inspect ' \
'and transform your json: <a href="https://jsoneditoronline.org/">JSONEditorOnline</a>' \
'\n\n4. Any questions left, run into some issues or you just want to check my codebase? Welcome ' \
HELP_INFO = (
"1. Exporting history."
"\nIn Telegram you can easily export your chat history. "
"I can only work with personal chats for now, so please pick one "
"and let's analyze it! "
'Here is an <a href="https://telegram.org/blog/export-and-more">official guide</a> on how to '
"get a file with your history. "
"Please note, this feature available only from Desktop App. Remember "
"to save in .json format, not in .html."
"\n\n2. How it works."
"\nAfter you done with exporting desired history - "
'we ready to go. Just attach "result.json" file to a regular message and send '
"it to me. I'll try to do my best analyzing it for you and send back some plots."
"My code is open and I do not collect any parts of your data, "
"but if you worry about it you can consider next step."
"\n\n3. You should feel free to delete essential "
"parts of your messaging history (like messages <i>text, user id,</i> etc) before sending it to me"
"\nI'll try to stay functional. "
"But there is recommended set of messages params "
"to be present in your json: <i>id, type, date, from, media_type</i>. "
"Here is some resources were you can inspect "
'and transform your json: <a href="https://jsoneditoronline.org/">JSONEditorOnline</a>'
"\n\n4. Any questions left, run into some issues or you just want to check my codebase? Welcome "
'to my repo on <a href="https://github.com/Flantropy/TelegramChatAnalyzer"> GitHub</a>'
)
26 changes: 16 additions & 10 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,39 @@
import telegram.ext as tg

from src.commands import (
analyze_history, help_info, pre_checkout_callback, shipping_callback, start,
start_noshipping_callback, start_shipping_callback, successful_payment,
analyze_history,
help_info,
pre_checkout_callback,
shipping_callback,
start,
start_noshipping_callback,
start_shipping_callback,
successful_payment,
)


def main():
# Setting up a basic logger
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Creating updater and dispatcher for bot
updater = tg.Updater(token=os.getenv('BOT_TOKEN'))
updater = tg.Updater(token=os.getenv("BOT_TOKEN"))
dispatcher: tg.Dispatcher = updater.dispatcher

# Filters
json_only_filter = tg.Filters.document.file_extension('json')
json_only_filter = tg.Filters.document.file_extension("json")

# Creating handlers
handlers = [
tg.CommandHandler('start', start),
tg.CommandHandler('help', help_info),
tg.CommandHandler('shipping', start_shipping_callback),
tg.CommandHandler('noshipping', start_noshipping_callback),
tg.CommandHandler("start", start),
tg.CommandHandler("help", help_info),
tg.CommandHandler("shipping", start_shipping_callback),
tg.CommandHandler("noshipping", start_noshipping_callback),
tg.PreCheckoutQueryHandler(pre_checkout_callback),
tg.ShippingQueryHandler(shipping_callback),
tg.MessageHandler(json_only_filter, analyze_history),
Expand All @@ -46,5 +52,5 @@ def main():
updater.idle()


if __name__ == '__main__':
if __name__ == "__main__":
main()
65 changes: 29 additions & 36 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ def _unpack_telegram_document(update) -> dict:
def _form_data_frame_from_json(chat_json) -> Optional[pd.DataFrame]:
try:
messages_df = pd.DataFrame(
chat_json['messages'],
columns=['id', 'type', 'date', 'from', 'text', 'media_type'])
chat_json["messages"],
columns=["id", "type", "date", "from", "text", "media_type"],
)
except KeyError as e:
logging.getLogger().error(
msg=f'Unable to form DataFrame from json. '
f'Key "messages" not found. {e}'
msg=f"Unable to form DataFrame from json. " f'Key "messages" not found. {e}'
)
return
else:
messages_df.set_index('id', inplace=True)
messages_df['date'] = pd.to_datetime(messages_df['date'])
messages_df.set_index("id", inplace=True)
messages_df["date"] = pd.to_datetime(messages_df["date"])
return messages_df


Expand All @@ -51,64 +51,57 @@ def _make_barplot(messages_df: pd.DataFrame) -> InputMediaPhoto:
:param messages_df: DataFrame with user messaging history
:return: telegram.InputMediaPhoto
"""
messages_per_month = messages_df['date'] \
.groupby(messages_df['date'].dt.to_period('M')) \
.agg('count')
messages_per_month = (
messages_df["date"].groupby(messages_df["date"].dt.to_period("M")).agg("count")
)
plot = sns.barplot(
x=messages_per_month.index,
y=messages_per_month.values,
color=(0.44, 0.35, 0.95)
color=(0.44, 0.35, 0.95),
)
plt.xticks(rotation=45)
plt.title('All time history')
plt.title("All time history")
return __convert_plot_to_telegram_photo(plot)


def _make_kde_plot(messages_df: pd.DataFrame) -> InputMediaPhoto:
plot = sns.kdeplot(
x=messages_df['date'],
hue=messages_df['from'],
shade=True
)
plt.title('Activity by user')
plot = sns.kdeplot(x=messages_df["date"], hue=messages_df["from"], shade=True)
plt.title("Activity by user")
plt.xticks(rotation=45)
plt.xlabel('')
plt.xlabel("")
return __convert_plot_to_telegram_photo(plot)


def _make_media_distribution_bar_plot(messages_df: pd.DataFrame) -> Optional[InputMediaPhoto]:
logging.getLogger().info('Enter media dist function')
media_dist_df = messages_df[['from', 'media_type']].value_counts()
def _make_media_distribution_bar_plot(
messages_df: pd.DataFrame,
) -> Optional[InputMediaPhoto]:
logging.getLogger().info("Enter media dist function")
media_dist_df = messages_df[["from", "media_type"]].value_counts()
if media_dist_df.empty:
return
media_dist_plot = media_dist_df.unstack().plot(
kind='bar',
stacked=True,
ylabel='Media messages',
xlabel='User'
kind="bar", stacked=True, ylabel="Media messages", xlabel="User"
)
plt.xticks(rotation=0)
plt.title('Distribution of media messages')
plt.title("Distribution of media messages")
return __convert_plot_to_telegram_photo(media_dist_plot)


def _make_weekday_distribution_bar_plot(messages_df: pd.DataFrame) -> InputMediaPhoto:
dist_by_day_of_week = messages_df['from']\
.groupby(messages_df['date'].dt.weekday)\
.agg('value_counts')
plot = dist_by_day_of_week.unstack().plot(kind='bar')
plt.xlabel('')
plt.ylabel('Messages')
dist_by_day_of_week = (
messages_df["from"].groupby(messages_df["date"].dt.weekday).agg("value_counts")
)
plot = dist_by_day_of_week.unstack().plot(kind="bar")
plt.xlabel("")
plt.ylabel("Messages")
plt.xticks(
list(range(7)),
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
rotation=0
list(range(7)), ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], rotation=0
)
return __convert_plot_to_telegram_photo(plot)


def make_plots(messages_df: pd.DataFrame) -> List[InputMediaPhoto]:
sns.set_theme(context='paper')
sns.set_theme(context="paper")
photo_list = [
_make_barplot(messages_df),
_make_media_distribution_bar_plot(messages_df),
Expand Down