diff --git a/pagermaid/__init__.py b/pagermaid/__init__.py index cf6abbf..8ce5830 100644 --- a/pagermaid/__init__.py +++ b/pagermaid/__init__.py @@ -13,7 +13,7 @@ from pagermaid.scheduler import scheduler import pyromod.listen from pyrogram import Client -pgm_version = "1.2.10" +pgm_version = "1.2.11" CMD_LIST = {} module_dir = __path__[0] working_dir = getcwd() diff --git a/pagermaid/modules/mixpanel.py b/pagermaid/modules/mixpanel.py index 08d2b25..84b8d5e 100644 --- a/pagermaid/modules/mixpanel.py +++ b/pagermaid/modules/mixpanel.py @@ -1,29 +1,113 @@ -from pagermaid import Config +import contextlib +import datetime +import json +import time +import uuid + +from pagermaid import Config, logs from pagermaid.enums import Client, Message +from pagermaid.services import client as request from pagermaid.hook import Hook -from mixpanel import Mixpanel -from mixpanel_async import AsyncBufferedConsumer -mp = Mixpanel(Config.MIXPANEL_API, consumer=AsyncBufferedConsumer()) +class DatetimeSerializer(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, datetime.datetime): + fmt = '%Y-%m-%dT%H:%M:%S' + return obj.strftime(fmt) + + return json.JSONEncoder.default(self, obj) + + +class Mixpanel: + def __init__(self, token: str): + self._token = token + self._serializer = DatetimeSerializer + self._request = request + self.api_host = "api.mixpanel.com" + + @staticmethod + def _now(): + return time.time() + + @staticmethod + def _make_insert_id(): + return uuid.uuid4().hex + + @staticmethod + def json_dumps(data, cls=None): + # Separators are specified to eliminate whitespace. + return json.dumps(data, separators=(',', ':'), cls=cls) + + async def api_call(self, endpoint, json_message): + _endpoints = { + 'events': f'https://{self.api_host}/track', + 'people': f'https://{self.api_host}/engage', + } + request_url = _endpoints.get(endpoint) + if request_url is None: + return + params = { + 'data': json_message, + 'verbose': 1, + 'ip': 0, + } + start = self._now() + with contextlib.suppress(Exception): + await self._request.post( + request_url, + data=params, + timeout=10.0 + ) + logs.debug(f"Mixpanel request took {self._now() - start} seconds") + + async def people_set(self, distinct_id: str, properties: dict): + message = { + '$distinct_id': distinct_id, + '$set': properties, + } + record = {'$token': self._token, '$time': self._now()} | message + return await self.api_call('people', self.json_dumps(record, cls=self._serializer)) + + async def track(self, distinct_id: str, event_name: str, properties: dict): + all_properties = { + 'token': self._token, + 'distinct_id': distinct_id, + 'time': self._now(), + '$insert_id': self._make_insert_id(), + 'mp_lib': 'python', + '$lib_version': '4.10.0', + } + if properties: + all_properties |= properties + event = { + 'event': event_name, + 'properties': all_properties, + } + return await self.api_call('events', self.json_dumps(event, cls=self._serializer)) + + +mp = Mixpanel(Config.MIXPANEL_API) @Hook.on_startup() async def mixpanel_init_id(bot: Client): - me = await bot.get_me() - if me.username: - mp.people_set(str(me.id), {'$first_name': me.first_name, "username": me.username}) - else: - mp.people_set(str(me.id), {'$first_name': me.first_name}) + if not bot.me: + bot.me = await bot.get_me() + data = {'$first_name': bot.me.first_name} + if bot.me.username: + data["username"] = bot.me.username + bot.loop.create_task(mp.people_set(str(bot.me.id), data)) @Hook.command_postprocessor() async def mixpanel_report(bot: Client, message: Message, command): if not Config.ALLOW_ANALYTIC: return - me = await bot.get_me() + if not bot.me: + bot.me = await bot.get_me() sender_id = message.from_user.id if message.from_user else "" sender_id = message.sender_chat.id if message.sender_chat else sender_id if sender_id < 0 and message.outgoing: - sender_id = me.id - mp.track(str(sender_id), f'Function {command}', {'command': command, "bot_id": me.id}) + sender_id = bot.me.id + bot.loop.create_task(mp.track(str(sender_id), f'Function {command}', {'command': command, "bot_id": bot.me.id})) diff --git a/pagermaid/modules/sentry.py b/pagermaid/modules/sentry.py index 0ff4580..3cfe2ba 100644 --- a/pagermaid/modules/sentry.py +++ b/pagermaid/modules/sentry.py @@ -4,6 +4,7 @@ from subprocess import run, PIPE from time import time from pyrogram.errors import Unauthorized, UsernameInvalid +from sentry_sdk.integrations.httpx import HttpxIntegration from pagermaid import Config from pagermaid.enums import Client, Message @@ -34,16 +35,20 @@ sentry_sdk.init( release=sentry_sdk_git_hash, before_send=sentry_before_send, environment="production", + integrations=[ + HttpxIntegration(), + ], ) @Hook.on_startup() async def sentry_init_id(bot: Client): - me = await bot.get_me() - if me.username: - sentry_sdk.set_user({"id": me.id, "name": me.first_name, "username": me.username, "ip_address": "{{auto}}"}) - else: - sentry_sdk.set_user({"id": me.id, "name": me.first_name, "ip_address": "{{auto}}"}) + if not bot.me: + bot.me = await bot.get_me() + data = {"id": bot.me.id, "name": bot.me.first_name, "ip_address": "{{auto}}"} + if bot.me.username: + data["username"] = bot.me.username + sentry_sdk.set_user(data) @Hook.process_error() diff --git a/requirements.txt b/requirements.txt index 2821ffa..1e3d515 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -pyrogram==2.0.50 +pyrogram==2.0.51 TgCrypto>=1.2.3 Pillow>=8.4.0 pytz>=2021.3 @@ -8,7 +8,5 @@ psutil>=5.8.0 httpx apscheduler sqlitedict -casbin==1.17.1 -mixpanel==4.10.0 -mixpanel-py-async -sentry-sdk==1.9.8 +casbin>=1.17.1 +sentry-sdk>=1.9.8