# Path: src/schemas/messages.py
# class SendMethod(str, Enum):
#     """
#     Should match SEND_METHODS sql enum
#     """
# 
#     email_mandrill = 'email-mandrill'
#     email_ses = 'email-ses'
#     email_test = 'email-test'
#     sms_messagebird = 'sms-messagebird'
#     sms_test = 'sms-test'
# 
# Path: src/schemas/webhooks.py
# class BaseWebhook(BaseModel):
#     ts: datetime
#     status: MessageStatus
#     message_id: str
# 
#     def extra_json(self, sort_keys=False):
#         raise NotImplementedError()
# 
#     @validator('ts')
#     def add_tz(cls, v):
#         if v and not v.tzinfo:
#             return v.replace(tzinfo=timezone.utc)
#         return v
# 
# class MandrillWebhook(BaseModel):
#     events: List[MandrillSingleWebhook]


import hashlib
import json
import logging
from arq.utils import to_unix_ms
from buildpg import V, Values
from datetime import timezone
from enum import Enum
from foxglove import glove
from pydantic.datetime_parse import parse_datetime
from ua_parser.user_agent_parser import Parse as ParseUserAgent
from src.schemas.messages import SendMethod
from src.schemas.webhooks import BaseWebhook, MandrillWebhook
    cache_key = f'click-{link_id}-{ip}'
    with await ctx['redis'] as redis:
        v = await redis.incr(cache_key)
        if v > 1:
            return 'recently_clicked'
        await redis.expire(cache_key, 60)

    url, message_id = await glove.pg.fetchrow_b(
        'select url, message_id from links where :where', where=V('id') == link_id
    )
    extra = {'target': url, 'ip': ip, 'user_agent': user_agent}
    if user_agent:
        ua_dict = ParseUserAgent(user_agent)
        platform = ua_dict['device']['family']
        if platform in {'Other', None}:
            platform = ua_dict['os']['family']
        extra['user_agent_display'] = '{user_agent[family]} {user_agent[major]} on {platform}'.format(
            platform=platform, **ua_dict
        ).strip(' ')

        ts = parse_datetime(ts)
        if not ts.tzinfo:
            ts = ts.replace(tzinfo=timezone.utc)
        status = 'click'
        await glove.pg.execute_b(
            'insert into events (:values__names) values :values',
            values=Values(message_id=message_id, status=status, ts=ts, extra=json.dumps(extra)),
        )



# Path: src/schemas/messages.py
# class SendMethod(str, Enum):
#     """
#     Should match SEND_METHODS sql enum
#     """
# 
#     email_mandrill = 'email-mandrill'
#     email_ses = 'email-ses'
#     email_test = 'email-test'
#     sms_messagebird = 'sms-messagebird'
#     sms_test = 'sms-test'
# 
# Path: src/schemas/webhooks.py
# class BaseWebhook(BaseModel):
#     ts: datetime
#     status: MessageStatus
#     message_id: str
# 
#     def extra_json(self, sort_keys=False):
#         raise NotImplementedError()
# 
#     @validator('ts')
#     def add_tz(cls, v):
#         if v and not v.tzinfo:
#             return v.replace(tzinfo=timezone.utc)
#         return v
# 
# class MandrillWebhook(BaseModel):
#     events: List[MandrillSingleWebhook]


# Path: src/worker/webhooks.py
import hashlib
import json
import logging
from arq.utils import to_unix_ms
from buildpg import V, Values
from datetime import timezone
from enum import Enum
from foxglove import glove
from pydantic.datetime_parse import parse_datetime
from ua_parser.user_agent_parser import Parse as ParseUserAgent
from src.schemas.messages import SendMethod
from src.schemas.webhooks import BaseWebhook, MandrillWebhook

    cache_key = f'click-{link_id}-{ip}'
    with await ctx['redis'] as redis:
        v = await redis.incr(cache_key)
        if v > 1:
            return 'recently_clicked'
        await redis.expire(cache_key, 60)

    url, message_id = await glove.pg.fetchrow_b(
        'select url, message_id from links where :where', where=V('id') == link_id
    )
    extra = {'target': url, 'ip': ip, 'user_agent': user_agent}
    if user_agent:
        ua_dict = ParseUserAgent(user_agent)
        platform = ua_dict['device']['family']
        if platform in {'Other', None}:
            platform = ua_dict['os']['family']
        extra['user_agent_display'] = '{user_agent[family]} {user_agent[major]} on {platform}'.format(
            platform=platform, **ua_dict
        ).strip(' ')

        ts = parse_datetime(ts)
        if not ts.tzinfo:
            ts = ts.replace(tzinfo=timezone.utc)
        status = 'click'
        await glove.pg.execute_b(
            'insert into events (:values__names) values :values',
            values=Values(message_id=message_id, status=status, ts=ts, extra=json.dumps(extra)),
        )



async def update_message_status(ctx, send_method: SendMethod, m: BaseWebhook, log_each=True) -> UpdateStatus: