async def send_reminder(client, timer: Timer): handles.pop(timer.id, None) del data['timers'][timer.id] storage.save() channel = client.get_channel(timer.channel_id) if not channel: logging.error(f"Cannot find channel for {timer}") return text = timer.text or "I am visiting from the past to remind you of something." text = f"<@{timer.user_id}> {text}" now = datetime.now() if now > timer.timestamp + timedelta(seconds=5): text = f"{text} ({format_timedelta(now - timer.timestamp)} late)" reference = discord.MessageReference( message_id=timer.message_id, channel_id=timer.channel_id, guild_id=timer.guild_id, fail_if_not_exists=False, ) await channel.send( text, reference=reference, # Generally we want to allow mentions, # but no power abuse through the bot's permissions allowed_mentions=NO_EVERYONE_MENTIONS, mention_author=True, )
async def test(ctx): try: jopa = discord.MessageReference('34') await ctx.send(jopa) except Exception as e: await ctx.send(e) return
async def send_reminder(user_id: int, reminder: Reminder) -> None: guild = discord_client.client.get_guild(reminder["guild"]) if guild is None: logger.info( "Reminder {} for user {} silently removed (guild no longer exists)" .format(str(reminder), user_id)) return channel = guild.get_channel_or_thread(reminder["channel"]) if not isinstance(channel, (discord.TextChannel, discord.Thread)): logger.info( "Reminder {} for user {} silently removed (channel no longer exists)" .format(str(reminder), user_id)) return try: creation_time = discord.utils.snowflake_time( reminder["msg"]).replace(tzinfo=timezone.utc) await channel.send( util.discord.format("{!m} asked to be reminded <t:{}:R>: {}", user_id, int(creation_time.timestamp()), reminder["contents"])[:2000], reference=discord.MessageReference(message_id=reminder["msg"], channel_id=reminder["channel"], fail_if_not_exists=False), allowed_mentions=discord.AllowedMentions( everyone=False, users=[discord.Object(user_id)], roles=False)) except discord.Forbidden: logger.info( "Reminder {} for user {} silently removed (permission error)". format(str(reminder), user_id))
async def run(ctx: commands.Context): l1 = await ctx.send(content="test test test", ) l2 = await ctx.send(content="test test test", ) last_at1 = {"incr": 0, "timestamp": 0} last_at2 = {"incr": 0, "timestamp": 1} while True: od = snowflake_to_dict(l1.id) nd = snowflake_to_dict(l2.id) diff = { "timestamp": nd["timestamp"] - od["timestamp"], "iw": nd["iw"] - od["iw"], "ip": nd["ip"] - od["ip"], "incr": nd["incr"] - od["incr"], } # print("l1/l2: " + str(diff)) pd = { "timestamp": nd["timestamp"] + diff["timestamp"], "iw": 2, "ip": nd["ip"] + diff["ip"], "incr": nd["incr"] + diff["incr"], } pd["incr"] = round((last_at2["incr"] / (last_at2["timestamp"] - last_at1["timestamp"])) * (pd["timestamp"] - last_at2["timestamp"])) # print("l2: " + str(snowflake_to_dict(l2.id))) # print("pd: " + str(pd)) pred_id = dict_to_snowflake(pd) l1 = l2 l2 = await ctx.send( content= f"https://discord.com/channels/{ctx.guild.id}/{ctx.channel.id}/{pred_id}" ) if snowflake_to_dict(l2.id)["iw"] == 2: last_at1 = last_at2 last_at2 = { "incr": snowflake_to_dict(l2.id)["incr"], "timestamp": snowflake_to_dict(l2.id)["timestamp"], } if l2.id == pred_id: l2 = await ctx.send( reference=discord.MessageReference( message_id=ctx.message.id, channel_id=ctx.channel.id), content= "IT WORKS THE PREVIOUS UNEDITED MESSAGE LINKS TO ITSELF <@234020040830091265>", ) break else: nd = snowflake_to_dict(l2.id) od = snowflake_to_dict(pred_id) print(l2.id) print(pred_id) print("l2/pred: " + str({ "timestamp": nd["timestamp"] - od["timestamp"], "iw": nd["iw"] - od["iw"], "ip": nd["ip"] - od["ip"], "incr": nd["incr"] - od["incr"], }))
async def process_reference( self, reference: Optional[discord.MessageReference], channel: UnionChannel, ) -> Optional[discord.MessageReference]: if not reference: return None # discord.py-stubs has a bad typehint for MessageReference.__init__ async for de, to in AsyncIter(self.messages.vectors(), steps=100): if de == (reference.channel_id, reference.message_id) and to[0] == channel.id: return discord.MessageReference( # type: ignore channel_id=channel.id, message_id=to[1] ) elif to == (reference.channel_id, reference.message_id) and de[0] == channel.id: return discord.MessageReference( # type: ignore channel_id=channel.id, message_id=de[1] ) return None
async def on_message(self, msg: discord.Message) -> None: if not msg.guild and self.user is not None and msg.author.id != self.user.id: try: guild = discord_client.client.get_guild(int(conf.guild)) if guild is None: return channel = guild.get_channel(int(conf.channel)) if not isinstance(channel, (discord.TextChannel, discord.Thread)): return role = guild.get_role(int(conf.role)) if role is None: return except (ValueError, AttributeError): return thread_id = await update_thread(msg.author.id) header = util.discord.format("**From {}#{}** {} {!m} on {}:\n\n", msg.author.name, msg.author.discriminator, msg.author.id, msg.author, msg.created_at) footer = "".join( "\n**Attachment:** {} {}".format(att.filename, att.url) for att in msg.attachments) if thread_id is None: footer += util.discord.format("\n{!m}", role) if footer: footer = "\n" + footer text = msg.content mentions = discord.AllowedMentions.none() mentions.roles = [role] reference = None if thread_id is not None: reference = discord.MessageReference(message_id=thread_id, channel_id=channel.id, fail_if_not_exists=False) copy_first = None sent_footer = False for i in range(0, len(header) + len(text), 2000): part = (header + text)[i:i + 2000] if len(part) + len(footer) <= 2000: part += footer sent_footer = True copy = await channel.send( part, allowed_mentions=mentions, reference=reference if copy_first is None else None) await add_modmail(msg, copy) if copy_first is None: copy_first = copy if not sent_footer: copy = await channel.send(footer, allowed_mentions=mentions) await add_modmail(msg, copy) if thread_id is None and copy_first is not None: await create_thread(msg.author.id, copy_first.id) await msg.add_reaction("\u2709")
async def bind_channel_envoi(msg): if msg.content.startswith(BLANK): return if msg.channel.id in BINDED_CHANNELS: auteur, texte, files = msg.author, msg.content, [ resendFile(x.url, x.filename) for x in msg.attachments ] embeds = msg.embeds reference = msg.reference embed = None if embeds == [] else embeds[0] texteRenvoye = BLANK + "**@{} :**\n{}".format( auteur.nick or auteur.name, texte) MSG_RETRANSMIS[msg.id] = (auteur, dict(), msg) for serveurCible, salonCible in BINDED_CHANNELS[msg.channel.id]: serveur = bot.get_guild(serveurCible) channel = serveur.get_channel(salonCible) if reference: refId = reference.message_id #il y a deux cas #1. soit on fait référence à un "vrai" message qui a été retransmis #2. soit on fait référence à un écho #1. if refId in MSG_RETRANSMIS: refEcho = MSG_RETRANSMIS[refId][1][salonCible].id chanRef = channel.id #2. elif refId in ECHO2MSG: refEcho, _ = ECHO2MSG[refId] chanRef = channel.id objRef = discord.MessageReference(message_id=refEcho, channel_id=chanRef) retransmis = await channel.send(texteRenvoye, reference=objRef, files=files, embed=embed) else: retransmis = await channel.send(texteRenvoye, files=files, embed=embed) MSG_RETRANSMIS[msg.id][1][salonCible] = retransmis ECHO2MSG[retransmis.id] = (msg.id, msg.channel.id) sleep(0.5) for x in files: supprFichier(x)
async def process_reference( self, reference: Optional[discord.MessageReference], channel: UnionChannel, ) -> Optional[discord.MessageReference]: if not reference: return None async for de, to in AsyncIter(self.messages.vectors(), steps=100): if (de == (reference.guild_id, reference.channel_id, reference.message_id) and to[1] == channel.id): return discord.MessageReference( guild_id=to[0], channel_id=to[1], message_id=to[2], fail_if_not_exists=False, ) elif (to == (reference.guild_id, reference.channel_id, reference.message_id) and de[1] == channel.id): return discord.MessageReference(guild_id=de[0], channel_id=de[1], message_id=de[2], fail_if_not_exists=False) return None
async def modmail_reply(self, msg: discord.Message) -> None: if msg.reference and msg.reference.message_id in message_map and not msg.author.bot: modmail = message_map[msg.reference.message_id] anon_react = "\U0001F574" named_react = "\U0001F9CD" cancel_react = "\u274C" try: query = await msg.channel.send( "Reply anonymously {}, personally {}, or cancel {}".format( anon_react, named_react, cancel_react)) except (discord.NotFound, discord.Forbidden): return result = await plugins.reactions.get_reaction( query, msg.author, { anon_react: "anon", named_react: "named", cancel_react: None }, timeout=120, unreact=False) await query.delete() if result is None: await msg.channel.send("Cancelled") else: header = "" if result == "named": header = util.discord.format("**From {}** {!m}:\n\n", msg.author.display_name, msg.author) try: chan = await client.fetch_channel(modmail.dm_channel_id) if not isinstance(chan, discord.DMChannel): await msg.channel.send( "Could not deliver DM (DM closed)") return await chan.send(header + msg.content, reference=discord.MessageReference( message_id=modmail.dm_message_id, channel_id=modmail.dm_channel_id, fail_if_not_exists=False)) except (discord.NotFound, discord.Forbidden): await msg.channel.send( "Could not deliver DM (User left guild?)") else: await msg.channel.send("Message delivered")
async def post_slash_command_warning(self, command_name, message): channel = message.channel guild = message.guild embed = discord.Embed( title=f"Please use /{command_name} to execute this command!", description= f"Discord is requiring **all bots** to switch to Slash Commands (which Bloxlink already supports!).\n\nPlease use `/{command_name}` instead to execute this command." "\n\n**Don't see Slash Commands?** Get the Server Admins to Re-authorize the bot here (__they don't need to kick it__): https://blox.link/invite and make sure the **Use Application Commands** permission is enabled for members.\n\n[Click here to see the Discord FAQ on Slash Commands](https://support.discord.com/hc/en-us/articles/1500000368501-Slash-Commands-FAQ)" ) embed.set_image(url="https://i.imgur.com/wVo8gt2.png") embed.colour = ORANGE_COLOR reference = discord.MessageReference(message_id=message and message.id, channel_id=channel.id, guild_id=guild and guild.id, fail_if_not_exists=False) try: await channel.send(embed=embed, reference=reference) except (discord.errors.NotFound, discord.errors.Forbidden): pass
async def on_message(self, msg: discord.Message) -> None: if msg.author.bot: return if not isinstance(msg.channel, (discord.abc.GuildChannel, discord.Thread)): return if not msg.content.startswith(conf.prefix): return if not plugins.locations.in_location("factoids", msg.channel): return text = " ".join(msg.content[len(conf.prefix):].split()).lower() if not len(text): return async with sqlalchemy.ext.asyncio.AsyncSession(engine) as session: stmt = (sqlalchemy.select(Alias).where( Alias.name == sqlalchemy.func.substring( text, 1, sqlalchemy.func.length(Alias.name))).order_by( sqlalchemy.func.length(Alias.name).desc()).limit(1)) if (alias := (await session.execute(stmt)).scalar()) is None: return embed = discord.Embed.from_dict( alias.factoid.embed_data ) if alias.factoid.embed_data is not None else None reference = None if msg.reference is not None and msg.reference.message_id is not None: reference = discord.MessageReference( guild_id=msg.reference.guild_id, channel_id=msg.reference.channel_id, message_id=msg.reference.message_id, fail_if_not_exists=False) await msg.channel.send( alias.factoid.message_text, embed=embed, reference=reference, allowed_mentions=discord.AllowedMentions.none()) alias.factoid.uses += 1 alias.factoid.used_at = datetime.datetime.utcnow() alias.uses += 1 alias.used_at = datetime.datetime.utcnow() await session.commit()
channel = await messageable._get_channel() ref_data = CONFIG_CACHE.setdefault(str(channel.id), {}).setdefault(emoji, {}) if (smileysend_force_new_ref or (ref_msg_id := ref_data.get("message_id")) is None): emoji_count = (2000 + 1) // (len(emoji) + 1) ref_msg_content = " ".join( itertools.repeat(emoji, min(50, emoji_count))) ref_msg = await real_send(messageable, ref_msg_content) ref_data["message_id"] = ref_msg_id = ref_msg.id scope = CONFIG.custom(MSG_IDS, str(channel.id), emoji).message_id await scope.set(ref_msg_id) return discord.MessageReference(message_id=ref_msg_id, channel_id=channel.id) async def send_with_msg_ref( messageable: Messageable, content=None, *, reference=None, smileysend_emoji, smileysend_force_new_ref=False, **kwargs, ) -> discord.Message: if reference is None and REPLIES_ENABLED: reference = await get_msg_ref( messageable, smileysend_emoji, smileysend_force_new_ref=smileysend_force_new_ref,
async def _requests(self, dc_channel, webhook, msg): name = image = None reply_to = reply_ref = reply_embed = None embeds = [] files = [] if msg.user: name = msg.user.real_name or msg.user.username image = msg.user.avatar for i, attach in enumerate(msg.attachments or []): if isinstance(attach, immp.File): if attach.title: title = attach.title elif attach.type == immp.File.Type.image: title = "image_{}.png".format(i) elif attach.type == immp.File.Type.video: title = "video_{}.mp4".format(i) else: title = "file_{}".format(i) async with (await attach.get_content(self.session)) as img_content: # discord.py expects a file-like object with a synchronous read() method. # NB. The whole file is read into memory by discord.py anyway. files.append( discord.File(BytesIO(await img_content.read()), title)) elif isinstance(attach, immp.Location): embed = discord.Embed() embed.title = attach.name or "Location" embed.url = attach.google_map_url embed.description = attach.address embed.set_thumbnail(url=attach.google_image_url(80)) embed.set_footer( text="{}, {}".format(attach.latitude, attach.longitude)) embeds.append((embed, "sent a location")) elif isinstance(attach, immp.Message): resolved = await self.resolve_message(attach) embeds.append( (await DiscordMessage.to_embed(self, resolved), "sent a message")) if msg.reply_to: if isinstance(msg.reply_to, immp.Receipt): if msg.reply_to.channel.plug.network_id == self.network_id: guild_id = dc_channel.guild.id if dc_channel.guild else None reply_ref = discord.MessageReference( message_id=int(msg.reply_to.id), channel_id=dc_channel.id, guild_id=guild_id) if not reply_to: reply_to = msg.reply_to reply_embed = await DiscordMessage.to_embed(self, reply_to, True) if webhook and msg.user: # Sending via webhook: multiple embeds and files supported. requests = [] rich = None if reply_embed: # Webhooks can't reply to other messages, quote the target in an embed instead. # https://github.com/discord/discord-api-docs/issues/2251 embeds.append((reply_embed, None)) if msg.text: rich = msg.text.clone() if msg.action: for segment in rich: segment.italic = True if msg.edited: if rich: rich.append(immp.Segment(" ")) else: rich = immp.RichText() rich.append(immp.Segment("(edited)", italic=True)) text = None if rich: mark = DiscordRichText.to_markdown(self, rich, True) chunks = immp.RichText.chunked_plain(mark, 2000) if len(chunks) > 1: # Multiple messages required to accommodate the text. requests.extend( webhook.send(content=chunk, wait=True, username=name, avatar_url=image) for chunk in chunks) else: text = chunks[0] if text or embeds or files: requests.append( webhook.send(content=text, wait=True, username=name, avatar_url=image, files=files, embeds=[embed[0] for embed in embeds])) return requests else: # Sending via client: only a single embed per message. requests = [] text = embed = desc = None chunks = [] rich = msg.render(link_name=False, edit=msg.edited) or None if rich: mark = DiscordRichText.to_markdown(self, rich) text, *chunks = immp.RichText.chunked_plain(mark, 2000) if reply_embed and not reply_ref: embeds.append((reply_embed, None)) if len(embeds) == 1: # Attach the only embed to the message text. embed, desc = embeds.pop() if text or embed or files: # Primary message: set reference for reply-to if applicable. requests.append( dc_channel.send(content=text or desc, embed=embed, files=files, reference=reply_ref)) # Send the remaining text if multiple messages were required to accommodate it. requests.extend(dc_channel.send(content=chunk) for chunk in chunks) for embed, desc in embeds: # Send any additional embeds in their own separate messages. content = None if msg.user and desc: label = immp.Message(user=msg.user, text="sent {}".format(desc), action=True) content = DiscordRichText.to_markdown(self, label.render()) requests.append(dc_channel.send(content=content, embed=embed)) return requests
async def command_checks(self, command, prefix, response, guild_data, author, channel, locale, CommandArgs, message=None, guild=None, subcommand_attrs=None, slash_command=False): channel_id = str(channel.id) if channel else None donator_profile = None dm = not bool(guild) subcommand_attrs = subcommand_attrs or {} if guild: if getattr(command, "addon", False): enabled_addons = guild and await get_enabled_addons(guild ) or {} if str(command.addon) not in enabled_addons: raise CancelCommand if getattr(command.addon, "premium", False): donator_profile, _ = await get_features( discord.Object(id=guild.owner_id), guild=guild) if not donator_profile.features.get("premium"): await response.error( f"This add-on requires premium! You may use `{prefix}donate` for instructions on donating.\n" f"You may also disable this add-on with `{prefix}addon change`.", hidden=True) raise CancelCommand if RELEASE == "PRO" and command.name not in ( "donate", "transfer", "eval", "status", "prefix", "add-features", "stats"): if not donator_profile: donator_profile, _ = await get_features( discord.Object(id=guild.owner_id), guild=guild) if not donator_profile.features.get("pro"): await response.error( f"Server not authorized to use Pro. Please use the `{prefix}donate` command to see information on " "how to get Bloxlink Pro.", hidden=True) raise CancelCommand if not command.bypass_channel_perms: if command.premium_bypass_channel_perms and not donator_profile: donator_profile, _ = await get_features(author, guild=guild) ignored_channels = guild_data.get("ignoredChannels", {}) disabled_commands = guild_data.get("disabledCommands", {}) author_perms = author.guild_permissions if guild.owner_id != author.id and not ( author_perms.manage_guild or author_perms.administrator or discord.utils.find( lambda r: r.name in MAGIC_ROLES, author.roles) or (command.premium_bypass_channel_perms and donator_profile.features.get("premium"))): premium_upsell = "\n**Pro-tip:** Bloxlink Premium users can use this command in disabled channels! Learn more at https://patreon.com/bloxlink." if command.premium_bypass_channel_perms else "" ignored_channel = ignored_channels.get(channel_id) or ( channel.category and ignored_channels.get(str(channel.category.id))) bypass_roles = ignored_channel.get( "bypassRoles", []) if ignored_channel else [] if ignored_channel and not discord.utils.find( lambda r: str(r.id) in bypass_roles, author.roles): await response.send( f"The server admins have **disabled** all commands in channel {channel.mention}.{premium_upsell}", dm=True, hidden=True, strict_post=True, no_dm_post=True) if message: try: await message.delete() except (discord.errors.Forbidden, discord.errors.NotFound): pass raise CancelCommand if command.name in disabled_commands.get("global", []): await response.send( f"The server admins have **disabled** the command `{command.name}` globally.{premium_upsell}", dm=True, hidden=True, strict_post=True, no_dm_post=True) if message: try: await message.delete() except (discord.errors.Forbidden, discord.errors.NotFound): pass raise CancelCommand elif disabled_commands.get("channels", {}).get(channel_id, {}).get(command.name): await response.send( f"The server admins have **disabled** the command `{command.name}` in channel {channel.mention}.{premium_upsell}", dm=True, hidden=True, strict_post=True, no_dm_post=True) if message: try: await message.delete() except (discord.errors.Forbidden, discord.errors.NotFound): pass raise CancelCommand if not slash_command and getattr(command, "slash_only", False): embed = discord.Embed( title="Please use this command as a Slash Command!", description="This command can only be used " f"as a Slash Command. Please use `/{command.name}` instead to execute this command. Non-slash commands will be " "disappearing **April of 2022!**\n\nPlease note that **all bots** will be **required** to switch to Slash Commands by next April (which Bloxlink already supports).\n\n" "For the technical: [Discord announcement](https://support-dev.discord.com/hc/en-us/articles/4404772028055)" ) embed.set_image(url="https://i.imgur.com/IsrRp5U.png") embed.colour = ORANGE_COLOR reference = discord.MessageReference(message_id=message and message.id, channel_id=channel_id, guild_id=guild and guild.id, fail_if_not_exists=False) try: await channel.send(embed=embed, reference=reference, delete_after=20) except (discord.errors.NotFound, discord.errors.Forbidden): pass raise CancelCommand restriction = await get_restriction("users", author.id) if restriction: restrction_text = isinstance( restriction, str ) and f"has an active restriction for: `{restriction}`" or "has an active restriction from Bloxlink." await response.send(f"{author.mention} {restrction_text}", hidden=True) raise CancelCommand if command.cooldown and self.cache: redis_cooldown_key = f"cooldown_cache:{command.name}:{author.id}" if not donator_profile or ( donator_profile and not donator_profile.features.get("premium")): donator_profile, _ = await get_features(author) if not donator_profile.features.get("premium"): on_cooldown = await self.cache.get(redis_cooldown_key) if on_cooldown: cooldown_time = await self.redis.ttl(redis_cooldown_key) embed = discord.Embed(title="Slow down!") embed.description = "This command has a short cooldown since it's relatively expensive for the bot. " \ f"You'll need to wait **{cooldown_time}** more second(s).\n\nDid you know? " \ "**[Bloxlink Premium](https://www.patreon.com/join/bloxlink?)** subscribers NEVER " \ f"see any cooldowns. Find out more information with `{prefix}donate`." m = await response.send(embed=embed, hidden=True) if m: await asyncio.sleep(10) try: await m.delete() if message: await message.delete() except (discord.errors.NotFound, discord.errors.Forbidden): pass raise CancelCommand await self.cache.set(redis_cooldown_key, True, expire_time=command.cooldown) if not (command.dm_allowed or guild): await response.send( "This command does not support DM; please run it in a server.", hidden=True) raise CancelCommand try: await command.check_permissions(author, guild, locale, dm=dm, **subcommand_attrs) except PermissionError as e: if subcommand_attrs.get("allow_bypass"): CommandArgs.has_permission = False elif command.permissions.allow_bypass: CommandArgs.has_permission = False else: await response.error(e, hidden=True) raise CancelCommand except Message as e: message_type = "send" if e.type == "info" else e.type response_fn = getattr(response, message_type, response.send) if e.message: await response_fn(e, hidden=True) if subcommand_attrs.get("allow_bypass"): CommandArgs.has_permission = False elif command.permissions.allow_bypass: CommandArgs.has_permission = False else: raise CancelCommand else: CommandArgs.has_permission = True
async def reminder_finish_handler(self): """ Handles reminders expiring. """ # Grab finished stuff from the database db = await vbu.Database.get_connection() rows = await db( "SELECT * FROM reminders WHERE timestamp < TIMEZONE('UTC', NOW())") if not rows: await db.disconnect() return # Go through all finished reminders expired_reminders = [] for reminder in rows: channel_id = reminder["channel_id"] user_id = reminder["user_id"] message_id = reminder["message_id"] message = reminder["message"] reminder_id = reminder["reminder_id"] try: channel = self.bot.get_channel( channel_id) or await self.bot.fetch_channel(channel_id) except discord.HTTPException: channel = None sendable = { "content": f"<@{user_id}> reminder `{reminder_id}` triggered - {message}", "allowed_mentions": discord.AllowedMentions(users=[discord.Object(user_id)]), } if message_id: sendable.update({ "reference": discord.MessageReference(message_id=message_id, channel_id=channel_id), "mention_author": True, }) try: assert channel is not None try: await channel.send(**sendable) except Exception: sendable.pop("reference") await channel.send(**sendable) except (AssertionError, discord.Forbidden): try: user = self.bot.get_user( user_id) or await self.bot.fetch_user(user_id) await user.send(**sendable) except discord.HTTPException: pass except AttributeError: pass expired_reminders.append(reminder_id) # Delete expired reminders await db("DELETE FROM reminders WHERE reminder_id=ANY($1::TEXT[])", expired_reminders) await db.disconnect()