async def put(self, channel, msg): if channel.source in self._hook.config["channels"]: ref = await self._hook.send(channel.source, msg) return [immp.Receipt(ref.key, channel)] else: raise immp.PlugError("Send to unknown sync channel: {}".format( repr(channel)))
async def delete(self, ref, sent=None): queue = [] for channel, ids in ref.ids.items(): for id_ in ids: if not (sent and sent.channel == channel and sent.id == id_): queue.append(immp.Receipt(id_, channel).delete()) if queue: await gather(*queue)
async def _parse_main(cls, slack, json, event, channel, parent=True, revision=None): id_, at = cls._parse_meta(slack, event) edited = bool(revision) deleted = False text = event["text"] user = await cls._parse_author(slack, event) action = False reply_to = joined = left = title = None attachments = [] if user and text and re.match(r"<@{}(\|.*?)?> ".format(user.id), text): # Own username at the start of the message, assume it's an action. action = True text = re.sub(r"^<@{}(\|.*?)?> ".format(user.id), "", text) if event["subtype"] in ("channel_join", "group_join"): action = True joined = [user] elif event["subtype"] in ("channel_leave", "group_leave"): action = True left = [user] elif event["subtype"] in ("channel_name", "group_name"): action = True title = event["name"] elif event["subtype"] == "me_message": action = True elif event["subtype"] == "reminder_add": action = True # Slack leaves a leading space in the message text: " set up a reminder..." text = text.lstrip() thread = recent = None if event["thread_ts"]: thread = immp.Receipt(event["thread_ts"], immp.Channel(slack, event["channel"])) if thread and parent: try: thread = await slack.get_message(thread, False) except MessageNotFound: pass if isinstance(thread, immp.Message): for reply in thread.raw["replies"][::-1]: if reply["ts"] not in (event["ts"], event["thread_ts"]): # Reply to a thread with at least one other message, use the next most # recent rather than the parent. recent = immp.Receipt(reply["ts"], channel) break if recent and parent: try: recent = await slack.get_reply(channel.source, event["thread_ts"], recent.id, False) except MessageNotFound: pass if thread and recent: # Don't walk the whole thread, just link to the parent after one step. recent.reply_to = thread reply_to = recent else: reply_to = recent or thread for file in event["files"]: try: attachments.append(SlackFile.from_file(slack, file)) except MessageNotFound: pass for attach in event["attachments"]: if attach["is_msg_unfurl"]: # We have the message ID as the timestamp, fetch the whole message to embed it. try: unfurl = cls.from_unfurl(slack, attach) attachments.append(await slack.resolve_message(unfurl)) except MessageNotFound: pass elif attach["image_url"]: attachments.append( immp.File(title=attach["title"], type_=immp.File.Type.image, source=attach["image_url"])) elif attach["fallback"]: if text: text = "{}\n---\n{}".format(text, attach["fallback"]) else: text = attach["fallback"] if text: # Messages can be shared either in the UI, or by pasting an archive link. The latter # unfurls async (it comes through as an edit, which we ignore), so instead we can look # up the message ourselves and embed it. regex = r"https://{}.slack.com/archives/([^/]+)/p([0-9]+)".format( slack._team["domain"]) for channel_id, link in re.findall(regex, text): # Archive links are strange and drop the period from the ts value. ts = link[:-6] + "." + link[-6:] refs = [ attach.id for attach in attachments if isinstance(attach, immp.Receipt) ] if ts not in refs: try: receipt = immp.Receipt(ts, immp.Channel(slack, channel_id)) attachments.append(await slack.resolve_message(receipt)) except MessageNotFound: pass if re.match("^<{}>$".format(regex), text): # Strip the message text if the entire body was just a link. text = None else: text = await SlackRichText.from_mrkdwn(slack, text) return immp.SentMessage(id_=id_, channel=channel, at=at, revision=revision, edited=edited, deleted=deleted, text=text, user=user, action=action, reply_to=reply_to, joined=joined, left=left, title=title, attachments=attachments, raw=json)
def from_unfurl(cls, slack, attach): unfurl = _Schema.msg_unfurl(attach) return immp.Receipt(unfurl["ts"], immp.Channel(slack, unfurl["channel_id"]))
async def from_message(cls, discord_, message, edited=False, deleted=False): """ Convert a :class:`discord.Message` into a :class:`.Message`. Args: discord (.DiscordPlug): Related plug instance that provides the event. message (discord.Message): Discord message object received from a channel. edited (bool): Whether this message comes from an edit event. deleted (bool): Whether this message comes from a delete event. Returns: .DiscordMessage: Parsed message object. """ text = reply_to = None channel = immp.Channel(discord_, message.channel.id) user = DiscordUser.from_user(discord_, message.author) attachments = [] if message.content: text = DiscordRichText.from_message(discord_, message) if message.reference: receipt = immp.Receipt( message.reference.message_id, immp.Channel(discord_, message.reference.channel_id)) reply_to = await discord_.get_message(receipt) for attach in message.attachments: if attach.filename.endswith((".jpg", ".png", ".gif")): type_ = immp.File.Type.image elif attach.filename.endswith((".mp4", ".webm")): type_ = immp.File.Type.video else: type_ = immp.File.Type.unknown attachments.append( immp.File(title=attach.filename, type_=type_, source=attach.url)) for embed in message.embeds: if embed.image.url and embed.image.url.rsplit( ".", 1)[1] in ("jpg", "png", "gif"): attachments.append( immp.File(type_=immp.File.Type.image, source=embed.image.url)) return immp.SentMessage( id_=message.id, channel=channel, # Timestamps are naive but in UTC. at=message.created_at.replace(tzinfo=timezone.utc), # Edited timestamp is blank for new messages, but updated in # existing objects when the message is later edited. revision=(message.edited_at or message.created_at).timestamp(), edited=edited, deleted=deleted, text=text, user=user, reply_to=reply_to, attachments=attachments, raw=message)