%first/%rand/%last image
This commit is contained in:
@@ -3,13 +3,60 @@ import random
|
|||||||
|
|
||||||
# Custom libs
|
# Custom libs
|
||||||
|
|
||||||
from utils import mention, from_now, str_datetime, message_link
|
from utils import mention, from_now, str_datetime, message_link, SPLIT_TOKEN
|
||||||
|
|
||||||
|
MAX_RANDOM_TRIES = 10
|
||||||
|
|
||||||
class History:
|
class History:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.messages = []
|
self.messages = []
|
||||||
|
|
||||||
|
async def to_string_image(self, *, type: str) -> List[str]:
|
||||||
|
if len(self.messages) == 0:
|
||||||
|
return ["There was no messages matching your filters"]
|
||||||
|
message = None
|
||||||
|
intro = None
|
||||||
|
real_message = None
|
||||||
|
if type == "first":
|
||||||
|
self.messages.sort(key=lambda m: m.created_at)
|
||||||
|
index = 0
|
||||||
|
while real_message is None and index < len(self.messages):
|
||||||
|
message = self.messages[index]
|
||||||
|
real_message = await message.fetch()
|
||||||
|
index += 1
|
||||||
|
intro = f"First image out of {len(self.messages):,}"
|
||||||
|
elif type == "last":
|
||||||
|
self.messages.sort(key=lambda m: m.created_at, reverse=True)
|
||||||
|
index = 0
|
||||||
|
while real_message is None and index < len(self.messages):
|
||||||
|
message = self.messages[index]
|
||||||
|
real_message = await message.fetch()
|
||||||
|
index += 1
|
||||||
|
intro = f"Last image out of {len(self.messages):,}"
|
||||||
|
elif type == "random":
|
||||||
|
intro = f"Random image out of {len(self.messages):,}"
|
||||||
|
tries = 0
|
||||||
|
while real_message is None and tries < MAX_RANDOM_TRIES:
|
||||||
|
message = random.choice(self.messages)
|
||||||
|
real_message = await message.fetch()
|
||||||
|
tries += 1
|
||||||
|
|
||||||
|
if real_message is None:
|
||||||
|
return ["There was no messages matching your filters"]
|
||||||
|
image = "<Error>"
|
||||||
|
if len(real_message.attachments) > 0:
|
||||||
|
image = real_message.attachments[0].url
|
||||||
|
elif len(real_message.embeds) > 0:
|
||||||
|
image = real_message.embeds[0].url
|
||||||
|
|
||||||
|
return [
|
||||||
|
intro,
|
||||||
|
f"{str_datetime(message.created_at)} ({from_now(message.created_at)}) {mention(message.author)} sent:",
|
||||||
|
f"<{message_link(message)}>",
|
||||||
|
SPLIT_TOKEN,
|
||||||
|
image,
|
||||||
|
]
|
||||||
|
|
||||||
def to_string(self, *, type: str) -> List[str]:
|
def to_string(self, *, type: str) -> List[str]:
|
||||||
if len(self.messages) == 0:
|
if len(self.messages) == 0:
|
||||||
return ["There was no messages matching your filters"]
|
return ["There was no messages matching your filters"]
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from typing import Union, Any
|
from typing import Optional, Union, Any
|
||||||
import discord
|
import discord
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from utils import is_extension, serialize
|
from utils import is_extension, serialize
|
||||||
|
|
||||||
IMAGE_FORMAT = [".gif", ".gifv", ".png", ".jpg", ".jpeg", ".bmp"]
|
IMAGE_FORMAT = [".gif", ".gifv", ".png", ".jpg", ".jpeg", ".bmp"]
|
||||||
EMBED_IMAGES = ["image", "gifv", "gif"]
|
EMBED_IMAGES = ["image", "gifv"]
|
||||||
|
|
||||||
|
|
||||||
class MessageLog:
|
class MessageLog:
|
||||||
@@ -76,6 +76,13 @@ class MessageLog:
|
|||||||
self.reactions[str(reaction.emoji)] = []
|
self.reactions[str(reaction.emoji)] = []
|
||||||
async for user in reaction.users():
|
async for user in reaction.users():
|
||||||
self.reactions[str(reaction.emoji)] += [user.id]
|
self.reactions[str(reaction.emoji)] += [user.id]
|
||||||
|
|
||||||
|
async def fetch(self) -> Optional[discord.Message]:
|
||||||
|
try:
|
||||||
|
return await self.channel.channel.fetch_message(self.id)
|
||||||
|
except (discord.NotFound, discord.Forbidden, discord.HTTPException):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def dict(self) -> dict:
|
def dict(self) -> dict:
|
||||||
return serialize(
|
return serialize(
|
||||||
|
|||||||
@@ -9,13 +9,17 @@ from utils import generate_help
|
|||||||
class FirstScanner(HistoryScanner):
|
class FirstScanner(HistoryScanner):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def help() -> str:
|
def help() -> str:
|
||||||
return generate_help("first", "Read first message (add text to filter like %find)")
|
return generate_help(
|
||||||
|
"first",
|
||||||
|
"Read first message (add text to filter like %find)",
|
||||||
|
args=["image - pull an image instead of a message"],
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(help=FirstScanner.help(), allow_queries=True)
|
super().__init__(help=FirstScanner.help())
|
||||||
|
|
||||||
def allow_message(self, *_) -> bool:
|
async def get_results(self, intro: str) -> List[str]:
|
||||||
return True
|
if self.images_only:
|
||||||
|
return await self.history.to_string_image(type="first")
|
||||||
def get_results(self, intro: str) -> List[str]:
|
else:
|
||||||
return self.history.to_string(type="first")
|
return self.history.to_string(type="first")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Callable, List
|
from typing import List, Tuple, Optional
|
||||||
import discord
|
import discord
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@@ -11,20 +11,20 @@ from logs import ChannelLogs, MessageLog
|
|||||||
|
|
||||||
|
|
||||||
class HistoryScanner(Scanner, ABC):
|
class HistoryScanner(Scanner, ABC):
|
||||||
def __init__(self, *, help: str, valid_args: List[str] = [], allow_queries: bool = False):
|
def __init__(self, *, help: str):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
has_digit_args=True,
|
has_digit_args=True,
|
||||||
valid_args=["all", "everyone"] + valid_args,
|
valid_args=["all", "everyone"],
|
||||||
help=help,
|
help=help,
|
||||||
intro_context="",
|
intro_context="",
|
||||||
all_args=allow_queries,
|
all_args=True,
|
||||||
)
|
)
|
||||||
self.allow_queries = allow_queries
|
|
||||||
|
|
||||||
async def init(self, message: discord.Message, *args: str) -> bool:
|
async def init(self, message: discord.Message, *args: str) -> bool:
|
||||||
self.history = History()
|
self.history = History()
|
||||||
self.all_messages = "all" in args or "everyone" in args
|
self.all_messages = "all" in args or "everyone" in args
|
||||||
if self.allow_queries:
|
self.images_only = "image" in args
|
||||||
|
if not self.images_only:
|
||||||
self.queries = [(query.lower(), query.strip("`") if re.match(r"^`.*`$", query) else None) for query in self.other_args]
|
self.queries = [(query.lower(), query.strip("`") if re.match(r"^`.*`$", query) else None) for query in self.other_args]
|
||||||
else:
|
else:
|
||||||
self.queries = []
|
self.queries = []
|
||||||
@@ -37,30 +37,14 @@ class HistoryScanner(Scanner, ABC):
|
|||||||
self.history,
|
self.history,
|
||||||
self.raw_members,
|
self.raw_members,
|
||||||
all_messages=self.all_messages,
|
all_messages=self.all_messages,
|
||||||
allow_message=lambda *args: self.allow_message(*args) and self.allow_message_query(*args),
|
queries=self.queries,
|
||||||
|
images_only=self.images_only,
|
||||||
)
|
)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_results(self, intro: str):
|
def get_results(self, intro: str):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def allow_message(self, channel: ChannelLogs, message: MessageLog) -> bool:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def allow_message_query(self, channel: ChannelLogs, message: MessageLog) -> bool:
|
|
||||||
if not self.allow_queries or len(self.queries) == 0:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
content = message.content.lower()
|
|
||||||
for query in self.queries:
|
|
||||||
if query[1] is not None:
|
|
||||||
if not re.match(query[1], message.content):
|
|
||||||
return False
|
|
||||||
elif not query[0] in content:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def analyse_message(
|
def analyse_message(
|
||||||
channel: ChannelLogs,
|
channel: ChannelLogs,
|
||||||
@@ -69,7 +53,8 @@ class HistoryScanner(Scanner, ABC):
|
|||||||
raw_members: List[int],
|
raw_members: List[int],
|
||||||
*,
|
*,
|
||||||
all_messages: bool,
|
all_messages: bool,
|
||||||
allow_message: Callable
|
queries: List[Tuple[str, Optional[str]]],
|
||||||
|
images_only: bool,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
impacted = False
|
impacted = False
|
||||||
# If author is included in the selection (empty list is all)
|
# If author is included in the selection (empty list is all)
|
||||||
@@ -80,8 +65,15 @@ class HistoryScanner(Scanner, ABC):
|
|||||||
or message.author in raw_members
|
or message.author in raw_members
|
||||||
)
|
)
|
||||||
and (message.content or message.attachment)
|
and (message.content or message.attachment)
|
||||||
and allow_message(channel, message)
|
and (not images_only or message.image)
|
||||||
):
|
):
|
||||||
|
content = message.content.lower()
|
||||||
|
for query in queries:
|
||||||
|
if query[1] is not None:
|
||||||
|
if not re.match(query[1], message.content):
|
||||||
|
return False
|
||||||
|
elif not query[0] in content:
|
||||||
|
return False
|
||||||
impacted = True
|
impacted = True
|
||||||
history.messages += [message]
|
history.messages += [message]
|
||||||
return impacted
|
return impacted
|
||||||
|
|||||||
@@ -9,13 +9,17 @@ from utils import generate_help
|
|||||||
class LastScanner(HistoryScanner):
|
class LastScanner(HistoryScanner):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def help() -> str:
|
def help() -> str:
|
||||||
return generate_help("last", "Read last message (add text to filter like %find)")
|
return generate_help(
|
||||||
|
"last",
|
||||||
|
"Read last message (add text to filter like %find)",
|
||||||
|
args=["image - pull an image instead of a message"],
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(help=LastScanner.help(), allow_queries=True)
|
super().__init__(help=LastScanner.help())
|
||||||
|
|
||||||
def allow_message(self, *_) -> bool:
|
async def get_results(self, intro: str) -> List[str]:
|
||||||
return True
|
if self.images_only:
|
||||||
|
return await self.history.to_string_image(type="last")
|
||||||
def get_results(self, intro: str) -> List[str]:
|
else:
|
||||||
return self.history.to_string(type="last")
|
return self.history.to_string(type="last")
|
||||||
|
|||||||
@@ -9,13 +9,17 @@ from utils import generate_help
|
|||||||
class RandomScanner(HistoryScanner):
|
class RandomScanner(HistoryScanner):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def help() -> str:
|
def help() -> str:
|
||||||
return generate_help("rand", "Read a random message (add text to filter like %find)")
|
return generate_help(
|
||||||
|
"rand",
|
||||||
|
"Read a random message (add text to filter like %find)",
|
||||||
|
args=["image - pull an image instead of a message"],
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(help=RandomScanner.help(), allow_queries=True)
|
super().__init__(help=RandomScanner.help())
|
||||||
|
|
||||||
def allow_message(self, *_) -> bool:
|
async def get_results(self, intro: str) -> List[str]:
|
||||||
return True
|
if self.images_only:
|
||||||
|
return await self.history.to_string_image(type="random")
|
||||||
def get_results(self, intro: str) -> List[str]:
|
else:
|
||||||
return self.history.to_string(type="random")
|
return self.history.to_string(type="random")
|
||||||
|
|||||||
+18
-5
@@ -4,6 +4,7 @@ from datetime import datetime
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import discord
|
import discord
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
from utils import (
|
from utils import (
|
||||||
@@ -16,6 +17,7 @@ from utils import (
|
|||||||
parse_time,
|
parse_time,
|
||||||
command_cache,
|
command_cache,
|
||||||
FilterLevel,
|
FilterLevel,
|
||||||
|
SPLIT_TOKEN,
|
||||||
)
|
)
|
||||||
from logs import (
|
from logs import (
|
||||||
GuildLogs,
|
GuildLogs,
|
||||||
@@ -241,8 +243,7 @@ class Scanner(ABC):
|
|||||||
await progress.edit(content="```Computing results...```")
|
await progress.edit(content="```Computing results...```")
|
||||||
# Display results
|
# Display results
|
||||||
t0 = datetime.now()
|
t0 = datetime.now()
|
||||||
results = self.get_results(
|
intro = get_intro(
|
||||||
get_intro(
|
|
||||||
self.intro_context,
|
self.intro_context,
|
||||||
self.full,
|
self.full,
|
||||||
self.channels,
|
self.channels,
|
||||||
@@ -252,7 +253,10 @@ class Scanner(ABC):
|
|||||||
self.start_date,
|
self.start_date,
|
||||||
self.stop_date,
|
self.stop_date,
|
||||||
)
|
)
|
||||||
)
|
if inspect.iscoroutinefunction(self.get_results):
|
||||||
|
results = await self.get_results(intro)
|
||||||
|
else:
|
||||||
|
results = self.get_results(intro)
|
||||||
logging.info(
|
logging.info(
|
||||||
f"scan {guild.id} > results in {delta(t0):,}ms"
|
f"scan {guild.id} > results in {delta(t0):,}ms"
|
||||||
)
|
)
|
||||||
@@ -265,7 +269,7 @@ class Scanner(ABC):
|
|||||||
)
|
)
|
||||||
for r in results:
|
for r in results:
|
||||||
if r:
|
if r:
|
||||||
if len(response + "\n" + r) > 2000:
|
if isinstance(r, int) and r == SPLIT_TOKEN:
|
||||||
await message.channel.send(
|
await message.channel.send(
|
||||||
response,
|
response,
|
||||||
reference=message if first else None,
|
reference=message if first else None,
|
||||||
@@ -273,7 +277,16 @@ class Scanner(ABC):
|
|||||||
)
|
)
|
||||||
first = False
|
first = False
|
||||||
response = ""
|
response = ""
|
||||||
response += "\n" + r
|
elif isinstance(r, str):
|
||||||
|
if len(response + "\n" + r) > 2000:
|
||||||
|
await message.channel.send(
|
||||||
|
response,
|
||||||
|
reference=message if first else None,
|
||||||
|
allowed_mentions=allowed_mentions,
|
||||||
|
)
|
||||||
|
first = False
|
||||||
|
response = ""
|
||||||
|
response += "\n" + r
|
||||||
if len(response) > 0:
|
if len(response) > 0:
|
||||||
await message.channel.send(
|
await message.channel.send(
|
||||||
response,
|
response,
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ class FilterLevel(Enum):
|
|||||||
ONLY = 2
|
ONLY = 2
|
||||||
|
|
||||||
|
|
||||||
|
SPLIT_TOKEN = 1152317803
|
||||||
|
|
||||||
|
|
||||||
# DISCORD API
|
# DISCORD API
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user