2020-07-21 12:15:40 +00:00
import asyncio , logging , aiohttp
2020-11-21 22:33:57 +00:00
from src . discord import send_to_discord_webhook , DiscordMessage , StackedDiscordMessage
2020-07-21 12:15:40 +00:00
from src . config import settings
2020-11-22 12:44:15 +00:00
from src . exceptions import EmbedListFull
2020-07-28 12:39:32 +00:00
from collections import defaultdict
2020-07-19 13:32:54 +00:00
logger = logging . getLogger ( " rcgcdw.msgqueue " )
class MessageQueue :
""" Message queue class for undelivered messages """
def __init__ ( self ) :
self . _queue = [ ]
2020-08-01 10:45:41 +00:00
self . global_rate_limit = False
2020-07-19 13:32:54 +00:00
def __repr__ ( self ) :
return self . _queue
def __len__ ( self ) :
return len ( self . _queue )
def __iter__ ( self ) :
return iter ( self . _queue )
def clear ( self ) :
self . _queue . clear ( )
def add_message ( self , message ) :
self . _queue . append ( message )
2020-11-30 13:21:54 +00:00
logger . debug ( " Adding new message " )
2020-11-28 13:08:37 +00:00
#
# def replace_message(self, to_replace: DiscordMessage, with_replace: StackedDiscordMessage):
# try:
# self._queue[self._queue.index(to_replace)] = with_replace
# except ValueError:
# raise
2020-11-22 12:44:15 +00:00
2020-07-19 13:32:54 +00:00
def cut_messages ( self , item_num ) :
self . _queue = self . _queue [ item_num : ]
2020-07-21 12:15:40 +00:00
2020-07-27 16:32:30 +00:00
async def group_by_webhook ( self ) : # TODO Change into iterable
""" Group Discord messages in the queue by the dictionary, allowing to send multiple messages to different
webhooks at the same time avoiding ratelimits per Discord webhook route . """
message_dict = defaultdict ( list )
for msg in self . _queue :
2020-09-13 15:02:02 +00:00
if not isinstance ( msg . webhook_url , list ) :
raise TypeError ( ' msg.webhook_url in _queue is not a list ' )
2020-07-27 16:32:30 +00:00
for webhook in msg . webhook_url :
2020-09-13 15:02:02 +00:00
message_dict [ webhook ] . append ( msg ) # defaultdict{"dadibadyvbdmadgqueh23/dihjd8agdandashd": [DiscordMessage, DiscordMessage]}
return message_dict . items ( ) # dict_items([('daosdkosakda/adkahfwegr34', [DiscordMessage]), ('daosdkosakda/adkahfwegr33', [DiscordMessage, DiscordMessage])])
2020-07-27 16:32:30 +00:00
async def send_msg_set ( self , msg_set : tuple ) :
2020-09-13 15:02:02 +00:00
webhook_url , messages = msg_set # str("daosdkosakda/adkahfwegr34", list(DiscordMessage, DiscordMessage, DiscordMessage)
2020-07-27 16:32:30 +00:00
for msg in messages :
2020-08-01 10:45:41 +00:00
if self . global_rate_limit :
return # if we are globally rate limited just wait for first gblocked request to finish
2020-08-01 01:02:58 +00:00
status = await send_to_discord_webhook ( msg , webhook_url )
if status [ 0 ] < 2 :
2020-07-27 16:32:30 +00:00
logger . debug ( " Sending message succeeded " )
2020-09-12 23:57:37 +00:00
try :
2020-11-30 13:21:54 +00:00
if len ( msg . webhook_url ) > 1 :
msg . webhook_url . remove ( webhook_url )
else :
self . _queue . remove ( msg )
2020-09-12 23:57:37 +00:00
except ValueError :
# For the love of god I cannot figure why can it return ValueError: list.remove(x): x not in list, however considering it's not in the list, somehow, anymore we can just not care about it I guess
pass
2020-08-01 01:15:31 +00:00
logger . debug ( " Current rate limit time: {} " . format ( status [ 1 ] ) )
2020-08-01 01:02:58 +00:00
if status [ 1 ] is not None :
2020-08-01 01:15:31 +00:00
await asyncio . sleep ( float ( status [ 1 ] ) ) # note, the timer on the last request won't matter that much since it's separate task and for the time of sleep it will give control to other tasks
2020-08-01 10:45:41 +00:00
break
elif status [ 0 ] == 5 :
if status [ 1 ] [ " global " ] is True :
logger . debug ( " Global rate limit has been detected. Setting global_rate_limit to true and awaiting punishment. " )
self . global_rate_limit = True
await asyncio . sleep ( status [ 1 ] [ " retry_after " ] / 1000 )
break
2020-07-27 16:32:30 +00:00
else :
logger . debug ( " Sending message failed " )
break
2020-07-19 13:32:54 +00:00
async def resend_msgs ( self ) :
2020-08-01 10:45:41 +00:00
self . global_rate_limit = False
2020-07-19 13:32:54 +00:00
if self . _queue :
logger . info (
2020-07-23 09:46:32 +00:00
" {} messages waiting to be delivered to Discord. " . format ( len ( self . _queue ) ) )
2020-07-27 16:32:30 +00:00
tasks_to_run = [ ]
for set_msgs in await self . group_by_webhook ( ) :
2020-08-12 12:05:34 +00:00
# logger.debug(set_msgs)
2020-07-27 16:32:30 +00:00
tasks_to_run . append ( self . send_msg_set ( set_msgs ) )
2020-09-13 15:02:02 +00:00
await asyncio . gather ( * tasks_to_run ) # we wait for all send_msg_set functions to finish
2020-08-01 10:45:41 +00:00
else :
await asyncio . sleep ( 0.5 )
2020-07-19 13:32:54 +00:00
2020-07-20 12:03:55 +00:00
messagequeue = MessageQueue ( )
async def send_to_discord ( msg ) :
2020-11-30 13:21:54 +00:00
messagequeue . add_message ( msg )
# webhooks = msg.webhook_url.copy()
# for webhook in webhooks:
# msg.webhook_url = [webhook] # Doing it just so it doesn't store reference but value
# messagequeue.add_message(msg.copy())