2025-11-14 04:03:20 +00:00
|
|
|
import logging
|
|
|
|
|
import datetime
|
2025-11-16 23:14:04 +00:00
|
|
|
import asyncio
|
2025-11-14 04:03:20 +00:00
|
|
|
|
|
|
|
|
from util import make_session
|
|
|
|
|
from generate import generate_order, generate_punishment
|
2026-03-05 03:01:35 +00:00
|
|
|
from db.queries import order_status_by_id, order_status_put, order_status_confirm
|
2025-11-14 04:03:20 +00:00
|
|
|
from mastodon import Mastodon
|
2025-11-16 16:34:52 +00:00
|
|
|
from telegram.telegram import Telegram
|
2026-03-07 02:26:56 +00:00
|
|
|
from settings import ENV
|
2025-11-14 04:03:20 +00:00
|
|
|
from util import timezone
|
2025-11-14 04:03:20 +00:00
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
async def order_mastodon_post(session, orders_pool, orders_str, repeats, due_at):
|
2026-03-06 01:48:04 +00:00
|
|
|
user = orders_pool.user
|
|
|
|
|
|
|
|
|
|
post = "Here are today's orders for @%s -\n\n" % user.mastodon_account()
|
2025-11-14 04:03:20 +00:00
|
|
|
post += orders_str + "\n\n"
|
2025-11-14 04:03:20 +00:00
|
|
|
if repeats > 1:
|
2025-11-14 04:03:20 +00:00
|
|
|
post += f"These are the same orders from the last {repeats} days\n\n"
|
2025-11-14 04:03:21 +00:00
|
|
|
post += "Proof of compliance is due by " + due_at.strftime("%I:%M %p") + "\n\n"
|
2026-03-06 01:48:04 +00:00
|
|
|
|
2025-11-14 04:03:21 +00:00
|
|
|
if ENV == 'dev':
|
|
|
|
|
post += "⚠️ DEV"
|
2026-03-06 03:47:39 +00:00
|
|
|
elif user.mastodon_attn_list:
|
|
|
|
|
post += f"ATTN - {user.mastodon_attn_list}\n"
|
2025-11-14 04:03:20 +00:00
|
|
|
|
|
|
|
|
m = Mastodon(session)
|
2025-11-14 04:03:20 +00:00
|
|
|
return await m.statusPost(post)
|
2025-11-14 04:03:20 +00:00
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
async def order_telegram_post(session, orders_pool, orders_str, repeats, due_at, m_url):
|
2025-11-14 04:03:20 +00:00
|
|
|
post = "Here are your orders -\n\n"
|
|
|
|
|
post += orders_str + "\n\n"
|
2025-11-14 04:03:20 +00:00
|
|
|
if repeats > 1:
|
2025-11-14 04:03:20 +00:00
|
|
|
post += f"These are the same orders from the last {repeats} days\n\n"
|
2025-11-14 04:03:21 +00:00
|
|
|
post += "Proof of compliance is due by " + due_at.strftime("%I:%M %p") + "\n\n"
|
2025-11-14 04:03:20 +00:00
|
|
|
post += m_url
|
2025-11-14 04:03:21 +00:00
|
|
|
if ENV == 'dev':
|
|
|
|
|
post += "\n⚠️ DEV"
|
2025-11-14 04:03:20 +00:00
|
|
|
|
|
|
|
|
t = Telegram(session)
|
2026-03-04 23:33:17 +00:00
|
|
|
await t.message_send(orders_pool.user.telegram_chat_id, post)
|
2025-11-14 04:03:20 +00:00
|
|
|
|
2026-03-06 02:16:42 +00:00
|
|
|
async def order_telegram_post_need_mastodon(session, orders_pool):
|
|
|
|
|
post = "Cannot issue an order without a mastodon username"
|
|
|
|
|
if ENV == 'dev':
|
|
|
|
|
post += "\n⚠️ DEV"
|
|
|
|
|
|
|
|
|
|
t = Telegram(session)
|
|
|
|
|
await t.message_send(orders_pool.user.telegram_chat_id, post)
|
|
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
async def order_telegram_post_none(session, orders_pool):
|
2025-11-14 04:03:21 +00:00
|
|
|
post = "No orders for today"
|
|
|
|
|
if ENV == 'dev':
|
|
|
|
|
post += "\n⚠️ DEV"
|
2025-11-14 04:03:20 +00:00
|
|
|
|
|
|
|
|
t = Telegram(session)
|
2026-03-04 23:33:17 +00:00
|
|
|
await t.message_send(orders_pool.user.telegram_chat_id, post)
|
2025-11-14 04:03:20 +00:00
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
async def order_issue(orders_pool):
|
2025-11-14 04:03:20 +00:00
|
|
|
async with make_session() as session:
|
2026-03-06 02:16:42 +00:00
|
|
|
if orders_pool.user.mastodon_username is None:
|
|
|
|
|
logger.info('Cannot issue order without mastodon username')
|
|
|
|
|
await order_telegram_post_need_mastodon(session, orders_pool)
|
|
|
|
|
return
|
|
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
orders_info = generate_order(orders_pool)
|
2025-11-14 04:03:20 +00:00
|
|
|
|
2025-11-14 04:03:20 +00:00
|
|
|
if 'orders' not in orders_info:
|
2025-11-14 04:03:20 +00:00
|
|
|
logger.info("No orders for today")
|
2026-03-04 23:33:17 +00:00
|
|
|
await order_telegram_post_none(session, orders_pool)
|
2025-11-14 04:03:20 +00:00
|
|
|
return
|
2025-11-14 04:03:20 +00:00
|
|
|
|
2025-11-14 04:03:20 +00:00
|
|
|
orders_str = "\n".join(orders_info['orders'])
|
|
|
|
|
|
2025-11-14 04:03:20 +00:00
|
|
|
created_at = datetime.datetime.now(tz=timezone())
|
2026-03-07 02:26:56 +00:00
|
|
|
if orders_pool.confirm_delay is not None:
|
|
|
|
|
due_at = created_at + datetime.timedelta(hours=orders_pool.confirm_delay)
|
2025-11-14 04:03:20 +00:00
|
|
|
|
|
|
|
|
repeats_count = orders_info.get('count', 0)
|
|
|
|
|
|
|
|
|
|
m_status = await order_mastodon_post(
|
|
|
|
|
session,
|
2026-03-04 23:33:17 +00:00
|
|
|
orders_pool,
|
2025-11-14 04:03:20 +00:00
|
|
|
orders_str,
|
|
|
|
|
repeats_count,
|
|
|
|
|
due_at
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await order_telegram_post(
|
|
|
|
|
session,
|
2026-03-04 23:33:17 +00:00
|
|
|
orders_pool,
|
2025-11-14 04:03:20 +00:00
|
|
|
orders_str,
|
|
|
|
|
repeats_count,
|
|
|
|
|
due_at,
|
|
|
|
|
m_status['url']
|
|
|
|
|
)
|
2025-11-14 04:03:20 +00:00
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
return order_status_put(
|
|
|
|
|
orders_pool,
|
|
|
|
|
orders_pool.user,
|
2025-11-14 04:03:20 +00:00
|
|
|
m_status['id'],
|
2025-11-14 04:03:20 +00:00
|
|
|
created_at,
|
|
|
|
|
due_at,
|
|
|
|
|
orders_str
|
2026-03-04 23:33:17 +00:00
|
|
|
)
|
2025-11-14 04:03:20 +00:00
|
|
|
|
2026-03-06 01:48:04 +00:00
|
|
|
async def punishment_mastodon_post(session, orders_pool, punishment_str, reply_id=None):
|
|
|
|
|
user = orders_pool.user
|
|
|
|
|
|
|
|
|
|
post = "@%s has failed to post proof of compliance. Here is the punishment -\n\n" % user.mastodon_account()
|
2025-11-14 04:03:21 +00:00
|
|
|
post += punishment_str + "\n\n"
|
2026-03-06 01:48:04 +00:00
|
|
|
|
2025-11-14 04:03:21 +00:00
|
|
|
if ENV == 'dev':
|
|
|
|
|
post += "⚠️ DEV"
|
2026-03-06 03:47:39 +00:00
|
|
|
elif user.mastodon_attn_list:
|
|
|
|
|
post += f"ATTN - {user.mastodon_attn_list}\n"
|
2025-11-14 04:03:20 +00:00
|
|
|
|
|
|
|
|
m = Mastodon(session)
|
|
|
|
|
return await m.statusPost(
|
|
|
|
|
post,
|
|
|
|
|
in_reply_to_id=reply_id
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-05 03:01:35 +00:00
|
|
|
async def punishment_telegram_post(session, orders_pool, punishment_str, m_url):
|
2025-11-14 04:03:20 +00:00
|
|
|
post = "You failed to show proof of compliance. Here is your punishment -\n\n"
|
|
|
|
|
post += punishment_str + "\n\n"
|
|
|
|
|
post += m_url
|
2025-11-14 04:03:21 +00:00
|
|
|
if ENV == 'dev':
|
|
|
|
|
post += "\n\n⚠️ DEV"
|
2025-11-14 04:03:20 +00:00
|
|
|
|
|
|
|
|
t = Telegram(session)
|
2026-03-05 03:01:35 +00:00
|
|
|
await t.message_send(orders_pool.user.telegram_chat_id, post)
|
2025-11-14 04:03:20 +00:00
|
|
|
|
2026-03-05 03:01:35 +00:00
|
|
|
async def punishment_issue(session, order_status):
|
|
|
|
|
if order_status.pool is None or order_status.pool.punishment_pool is None:
|
|
|
|
|
logger.info(f'Unable to issue a punishment for {order_status.id}, no punishment pool for order pool {order_status.pool.name}')
|
|
|
|
|
# TODO: No punishment mastodon/telegram posts
|
|
|
|
|
return
|
2026-03-04 23:33:17 +00:00
|
|
|
|
2026-03-05 03:01:35 +00:00
|
|
|
punishment_pool = order_status.pool.punishment_pool
|
2026-03-04 23:33:17 +00:00
|
|
|
|
2026-03-05 03:01:35 +00:00
|
|
|
punishment = generate_punishment(punishment_pool)
|
|
|
|
|
punishment_str = "\n".join(punishment['orders'])
|
2025-11-14 04:03:20 +00:00
|
|
|
|
|
|
|
|
punishment_status = await punishment_mastodon_post(
|
|
|
|
|
session,
|
2026-03-06 01:48:04 +00:00
|
|
|
punishment_pool,
|
2025-11-14 04:03:20 +00:00
|
|
|
punishment_str,
|
2026-03-05 03:01:35 +00:00
|
|
|
order_status.mastodon_id,
|
2025-11-14 04:03:20 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await punishment_telegram_post(
|
|
|
|
|
session,
|
2026-03-05 03:01:35 +00:00
|
|
|
punishment_pool,
|
2025-11-14 04:03:20 +00:00
|
|
|
punishment_str,
|
|
|
|
|
punishment_status['url']
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-05 03:01:35 +00:00
|
|
|
order_status_put(
|
|
|
|
|
punishment_pool,
|
|
|
|
|
order_status.user,
|
2025-11-14 04:03:20 +00:00
|
|
|
punishment_status['id'],
|
|
|
|
|
punishment_status['created_at'],
|
2026-03-05 03:01:35 +00:00
|
|
|
None,
|
|
|
|
|
punishment_str,
|
|
|
|
|
punishment_for=order_status
|
2025-11-14 04:03:20 +00:00
|
|
|
)
|
|
|
|
|
|
2025-11-16 23:14:04 +00:00
|
|
|
order_check_lock = asyncio.Lock()
|
2026-03-04 23:33:17 +00:00
|
|
|
async def order_check(order_status_id):
|
2025-11-16 23:14:04 +00:00
|
|
|
async with order_check_lock:
|
|
|
|
|
async with make_session() as session:
|
2026-03-04 23:33:17 +00:00
|
|
|
order_status = order_status_by_id(order_status_id)
|
|
|
|
|
|
2026-03-05 03:01:35 +00:00
|
|
|
if order_status.punishment.count() > 0:
|
|
|
|
|
logger.info(f'Punishment already issued for {order_status.id}')
|
|
|
|
|
return
|
|
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
m = Mastodon(session)
|
|
|
|
|
context = await m.statusContext(order_status.mastodon_id)
|
|
|
|
|
|
|
|
|
|
confirmed_at = None
|
|
|
|
|
for d in context['descendants']:
|
|
|
|
|
if (
|
|
|
|
|
d['in_reply_to_id'] == order_status.mastodon_id and
|
2026-03-06 03:47:39 +00:00
|
|
|
d['account']['acct'] == order_status.user.mastodon_account() and
|
2026-03-04 23:33:17 +00:00
|
|
|
len(d['media_attachments']) > 0
|
|
|
|
|
):
|
|
|
|
|
confirmed_at = d['created_at']
|
|
|
|
|
order_status_confirm(order_status.id, confirmed_at)
|
|
|
|
|
logger.info('Confirmed order %s' % (order_status.id))
|
|
|
|
|
break
|
2025-11-16 23:14:04 +00:00
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
if confirmed_at is None:
|
|
|
|
|
logger.info('Order %s remains unconfirmed' % (order_status.id))
|
2025-11-16 23:14:04 +00:00
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
due_at = datetime.datetime.fromisoformat(order_status.due_at)
|
|
|
|
|
if(due_at < datetime.datetime.now(datetime.UTC)):
|
|
|
|
|
logger.info('Time to issue a punishment for %s' % order_status.id)
|
2025-11-16 23:14:04 +00:00
|
|
|
|
2026-03-04 23:33:17 +00:00
|
|
|
await punishment_issue(session, order_status)
|