Ejemplo n.º 1
0
    def get_room_vars(self, room_id, user_id, notifs, notif_events,
                      room_state_ids):
        my_member_event_id = room_state_ids[("m.room.member", user_id)]
        my_member_event = yield self.store.get_event(my_member_event_id)
        is_invite = my_member_event.content["membership"] == "invite"

        room_name = yield calculate_room_name(self.store, room_state_ids,
                                              user_id)

        room_vars = {
            "title": room_name,
            "hash": string_ordinal_total(room_id),  # See sender avatar hash
            "notifs": [],
            "invite": is_invite,
            "link": self.make_room_link(room_id),
        }

        if not is_invite:
            for n in notifs:
                notifvars = yield self.get_notif_vars(
                    n, user_id, notif_events[n['event_id']], room_state_ids)

                # merge overlapping notifs together.
                # relies on the notifs being in chronological order.
                merge = False
                if room_vars['notifs'] and 'messages' in room_vars['notifs'][
                        -1]:
                    prev_messages = room_vars['notifs'][-1]['messages']
                    for message in notifvars['messages']:
                        pm = list(
                            filter(lambda pm: pm['id'] == message['id'],
                                   prev_messages))
                        if pm:
                            if not message["is_historical"]:
                                pm[0]["is_historical"] = False
                            merge = True
                        elif merge:
                            # we're merging, so append any remaining messages
                            # in this notif to the previous one
                            prev_messages.append(message)

                if not merge:
                    room_vars['notifs'].append(notifvars)

        defer.returnValue(room_vars)
    def _calculate_room_name(
        self,
        events: StateMap[dict],
        user_id: str = "",
        fallback_to_members: bool = True,
        fallback_to_single_member: bool = True,
    ):
        # This isn't 100% accurate, but works with MockDataStore.
        room_state_ids = {k[0]: k[0] for k in events}

        return self.get_success(
            calculate_room_name(
                MockDataStore(events),
                room_state_ids,
                user_id or self.USER_ID,
                fallback_to_members,
                fallback_to_single_member,
            ))
Ejemplo n.º 3
0
def get_context_for_event(store, state_handler, ev, user_id):
    ctx = {}

    room_state_ids = yield store.get_state_ids_for_event(ev.event_id)

    # we no longer bother setting room_alias, and make room_name the
    # human-readable name instead, be that m.room.name, an alias or
    # a list of people in the room
    name = yield calculate_room_name(
        store, room_state_ids, user_id, fallback_to_single_member=False
    )
    if name:
        ctx["name"] = name

    sender_state_event_id = room_state_ids[("m.room.member", ev.sender)]
    sender_state_event = yield store.get_event(sender_state_event_id)
    ctx["sender_display_name"] = name_from_member_event(sender_state_event)

    return ctx
Ejemplo n.º 4
0
def get_context_for_event(store, state_handler, ev, user_id):
    ctx = {}

    room_state_ids = yield state_handler.get_current_state_ids(ev.room_id)

    # we no longer bother setting room_alias, and make room_name the
    # human-readable name instead, be that m.room.name, an alias or
    # a list of people in the room
    name = yield calculate_room_name(
        store, room_state_ids, user_id, fallback_to_single_member=False
    )
    if name:
        ctx['name'] = name

    sender_state_event_id = room_state_ids[("m.room.member", ev.sender)]
    sender_state_event = yield store.get_event(sender_state_event_id)
    ctx['sender_display_name'] = name_from_member_event(sender_state_event)

    defer.returnValue(ctx)
Ejemplo n.º 5
0
    def get_room_vars(self, room_id, user_id, notifs, notif_events, room_state_ids):
        my_member_event_id = room_state_ids[("m.room.member", user_id)]
        my_member_event = yield self.store.get_event(my_member_event_id)
        is_invite = my_member_event.content["membership"] == "invite"

        room_name = yield calculate_room_name(self.store, room_state_ids, user_id)

        room_vars = {
            "title": room_name,
            "hash": string_ordinal_total(room_id),  # See sender avatar hash
            "notifs": [],
            "invite": is_invite,
            "link": self.make_room_link(room_id),
        }

        if not is_invite:
            for n in notifs:
                notifvars = yield self.get_notif_vars(
                    n, user_id, notif_events[n['event_id']], room_state_ids
                )

                # merge overlapping notifs together.
                # relies on the notifs being in chronological order.
                merge = False
                if room_vars['notifs'] and 'messages' in room_vars['notifs'][-1]:
                    prev_messages = room_vars['notifs'][-1]['messages']
                    for message in notifvars['messages']:
                        pm = list(filter(lambda pm: pm['id'] == message['id'],
                                         prev_messages))
                        if pm:
                            if not message["is_historical"]:
                                pm[0]["is_historical"] = False
                            merge = True
                        elif merge:
                            # we're merging, so append any remaining messages
                            # in this notif to the previous one
                            prev_messages.append(message)

                if not merge:
                    room_vars['notifs'].append(notifvars)

        defer.returnValue(room_vars)
Ejemplo n.º 6
0
    def make_summary_text(self, notifs_by_room, room_state_ids, notif_events,
                          user_id, reason):
        if len(notifs_by_room) == 1:
            # Only one room has new stuff
            room_id = notifs_by_room.keys()[0]

            # If the room has some kind of name, use it, but we don't
            # want the generated-from-names one here otherwise we'll
            # end up with, "new message from Bob in the Bob room"
            room_name = yield calculate_room_name(self.store,
                                                  room_state_ids[room_id],
                                                  user_id,
                                                  fallback_to_members=False)

            my_member_event_id = room_state_ids[room_id][("m.room.member",
                                                          user_id)]
            my_member_event = yield self.store.get_event(my_member_event_id)
            if my_member_event.content["membership"] == "invite":
                inviter_member_event_id = room_state_ids[room_id][(
                    "m.room.member", my_member_event.sender)]
                inviter_member_event = yield self.store.get_event(
                    inviter_member_event_id)
                inviter_name = name_from_member_event(inviter_member_event)

                if room_name is None:
                    defer.returnValue(INVITE_FROM_PERSON % {
                        "person": inviter_name,
                        "app": self.app_name
                    })
                else:
                    defer.returnValue(
                        INVITE_FROM_PERSON_TO_ROOM % {
                            "person": inviter_name,
                            "room": room_name,
                            "app": self.app_name,
                        })

            sender_name = None
            if len(notifs_by_room[room_id]) == 1:
                # There is just the one notification, so give some detail
                event = notif_events[notifs_by_room[room_id][0]["event_id"]]
                if ("m.room.member", event.sender) in room_state_ids[room_id]:
                    state_event_id = room_state_ids[room_id][("m.room.member",
                                                              event.sender)]
                    state_event = yield self.store.get_event(state_event_id)
                    sender_name = name_from_member_event(state_event)

                if sender_name is not None and room_name is not None:
                    defer.returnValue(
                        MESSAGE_FROM_PERSON_IN_ROOM % {
                            "person": sender_name,
                            "room": room_name,
                            "app": self.app_name,
                        })
                elif sender_name is not None:
                    defer.returnValue(MESSAGE_FROM_PERSON % {
                        "person": sender_name,
                        "app": self.app_name,
                    })
            else:
                # There's more than one notification for this room, so just
                # say there are several
                if room_name is not None:
                    defer.returnValue(MESSAGES_IN_ROOM % {
                        "room": room_name,
                        "app": self.app_name,
                    })
                else:
                    # If the room doesn't have a name, say who the messages
                    # are from explicitly to avoid, "messages in the Bob room"
                    sender_ids = list(
                        set([
                            notif_events[n['event_id']].sender
                            for n in notifs_by_room[room_id]
                        ]))

                    member_events = yield self.store.get_events([
                        room_state_ids[room_id][("m.room.member", s)]
                        for s in sender_ids
                    ])

                    defer.returnValue(
                        MESSAGES_FROM_PERSON % {
                            "person":
                            descriptor_from_member_events(
                                member_events.values()),
                            "app":
                            self.app_name,
                        })
        else:
            # Stuff's happened in multiple different rooms

            # ...but we still refer to the 'reason' room which triggered the mail
            if reason['room_name'] is not None:
                defer.returnValue(MESSAGES_IN_ROOM_AND_OTHERS % {
                    "room": reason['room_name'],
                    "app": self.app_name,
                })
            else:
                # If the reason room doesn't have a name, say who the messages
                # are from explicitly to avoid, "messages in the Bob room"
                sender_ids = list(
                    set([
                        notif_events[n['event_id']].sender
                        for n in notifs_by_room[reason['room_id']]
                    ]))

                member_events = yield self.store.get_events([
                    room_state_ids[room_id][("m.room.member", s)]
                    for s in sender_ids
                ])

                defer.returnValue(
                    MESSAGES_FROM_PERSON_AND_OTHERS % {
                        "person":
                        descriptor_from_member_events(member_events.values()),
                        "app":
                        self.app_name,
                    })
Ejemplo n.º 7
0
    def send_notification_mail(self, app_id, user_id, email_address,
                               push_actions, reason):
        try:
            from_string = self.hs.config.email_notif_from % {
                "app": self.app_name
            }
        except TypeError:
            from_string = self.hs.config.email_notif_from

        raw_from = email.utils.parseaddr(from_string)[1]
        raw_to = email.utils.parseaddr(email_address)[1]

        if raw_to == '':
            raise RuntimeError("Invalid 'to' address")

        rooms_in_order = deduped_ordered_list(
            [pa['room_id'] for pa in push_actions])

        notif_events = yield self.store.get_events(
            [pa['event_id'] for pa in push_actions])

        notifs_by_room = {}
        for pa in push_actions:
            notifs_by_room.setdefault(pa["room_id"], []).append(pa)

        # collect the current state for all the rooms in which we have
        # notifications
        state_by_room = {}

        try:
            user_display_name = yield self.store.get_profile_displayname(
                UserID.from_string(user_id).localpart)
            if user_display_name is None:
                user_display_name = user_id
        except StoreError:
            user_display_name = user_id

        @defer.inlineCallbacks
        def _fetch_room_state(room_id):
            room_state = yield self.store.get_current_state_ids(room_id)
            state_by_room[room_id] = room_state

        # Run at most 3 of these at once: sync does 10 at a time but email
        # notifs are much less realtime than sync so we can afford to wait a bit.
        yield concurrently_execute(_fetch_room_state, rooms_in_order, 3)

        # actually sort our so-called rooms_in_order list, most recent room first
        rooms_in_order.sort(
            key=lambda r: -(notifs_by_room[r][-1]['received_ts'] or 0))

        rooms = []

        for r in rooms_in_order:
            roomvars = yield self.get_room_vars(r, user_id, notifs_by_room[r],
                                                notif_events, state_by_room[r])
            rooms.append(roomvars)

        reason['room_name'] = yield calculate_room_name(
            self.store,
            state_by_room[reason['room_id']],
            user_id,
            fallback_to_members=True)

        summary_text = yield self.make_summary_text(notifs_by_room,
                                                    state_by_room,
                                                    notif_events, user_id,
                                                    reason)

        template_vars = {
            "user_display_name":
            user_display_name,
            "unsubscribe_link":
            self.make_unsubscribe_link(user_id, app_id, email_address),
            "summary_text":
            summary_text,
            "app_name":
            self.app_name,
            "rooms":
            rooms,
            "reason":
            reason,
        }

        html_text = self.notif_template_html.render(**template_vars)
        html_part = MIMEText(html_text, "html", "utf8")

        plain_text = self.notif_template_text.render(**template_vars)
        text_part = MIMEText(plain_text, "plain", "utf8")

        multipart_msg = MIMEMultipart('alternative')
        multipart_msg['Subject'] = "[%s] %s" % (self.app_name, summary_text)
        multipart_msg['From'] = from_string
        multipart_msg['To'] = email_address
        multipart_msg['Date'] = email.utils.formatdate()
        multipart_msg['Message-ID'] = email.utils.make_msgid()
        multipart_msg.attach(text_part)
        multipart_msg.attach(html_part)

        logger.info("Sending email push notification to %s" % email_address)
        # logger.debug(html_text)

        yield sendmail(
            self.hs.config.email_smtp_host,
            raw_from,
            raw_to,
            multipart_msg.as_string(),
            port=self.hs.config.email_smtp_port,
            requireAuthentication=self.hs.config.email_smtp_user is not None,
            username=self.hs.config.email_smtp_user,
            password=self.hs.config.email_smtp_pass,
            requireTransportSecurity=self.hs.config.require_transport_security)
Ejemplo n.º 8
0
    def send_notification_mail(
        self, app_id, user_id, email_address, push_actions, reason
    ):
        """Send email regarding a user's room notifications"""
        rooms_in_order = deduped_ordered_list([pa["room_id"] for pa in push_actions])

        notif_events = yield self.store.get_events(
            [pa["event_id"] for pa in push_actions]
        )

        notifs_by_room = {}
        for pa in push_actions:
            notifs_by_room.setdefault(pa["room_id"], []).append(pa)

        # collect the current state for all the rooms in which we have
        # notifications
        state_by_room = {}

        try:
            user_display_name = yield self.store.get_profile_displayname(
                UserID.from_string(user_id).localpart
            )
            if user_display_name is None:
                user_display_name = user_id
        except StoreError:
            user_display_name = user_id

        @defer.inlineCallbacks
        def _fetch_room_state(room_id):
            room_state = yield self.store.get_current_state_ids(room_id)
            state_by_room[room_id] = room_state

        # Run at most 3 of these at once: sync does 10 at a time but email
        # notifs are much less realtime than sync so we can afford to wait a bit.
        yield concurrently_execute(_fetch_room_state, rooms_in_order, 3)

        # actually sort our so-called rooms_in_order list, most recent room first
        rooms_in_order.sort(key=lambda r: -(notifs_by_room[r][-1]["received_ts"] or 0))

        rooms = []

        for r in rooms_in_order:
            roomvars = yield self.get_room_vars(
                r, user_id, notifs_by_room[r], notif_events, state_by_room[r]
            )
            rooms.append(roomvars)

        reason["room_name"] = yield calculate_room_name(
            self.store,
            state_by_room[reason["room_id"]],
            user_id,
            fallback_to_members=True,
        )

        summary_text = yield self.make_summary_text(
            notifs_by_room, state_by_room, notif_events, user_id, reason
        )

        template_vars = {
            "user_display_name": user_display_name,
            "unsubscribe_link": self.make_unsubscribe_link(
                user_id, app_id, email_address
            ),
            "summary_text": summary_text,
            "app_name": self.app_name,
            "rooms": rooms,
            "reason": reason,
        }

        yield self.send_email(
            email_address, "[%s] %s" % (self.app_name, summary_text), template_vars
        )
Ejemplo n.º 9
0
    def send_notification_mail(self, app_id, user_id, email_address,
                               push_actions, reason):
        try:
            from_string = self.hs.config.email_notif_from % {
                "app": self.app_name
            }
        except TypeError:
            from_string = self.hs.config.email_notif_from

        raw_from = email.utils.parseaddr(from_string)[1]
        raw_to = email.utils.parseaddr(email_address)[1]

        if raw_to == '':
            raise RuntimeError("Invalid 'to' address")

        rooms_in_order = deduped_ordered_list(
            [pa['room_id'] for pa in push_actions]
        )

        notif_events = yield self.store.get_events(
            [pa['event_id'] for pa in push_actions]
        )

        notifs_by_room = {}
        for pa in push_actions:
            notifs_by_room.setdefault(pa["room_id"], []).append(pa)

        # collect the current state for all the rooms in which we have
        # notifications
        state_by_room = {}

        try:
            user_display_name = yield self.store.get_profile_displayname(
                UserID.from_string(user_id).localpart
            )
            if user_display_name is None:
                user_display_name = user_id
        except StoreError:
            user_display_name = user_id

        @defer.inlineCallbacks
        def _fetch_room_state(room_id):
            room_state = yield self.state_handler.get_current_state_ids(room_id)
            state_by_room[room_id] = room_state

        # Run at most 3 of these at once: sync does 10 at a time but email
        # notifs are much less realtime than sync so we can afford to wait a bit.
        yield concurrently_execute(_fetch_room_state, rooms_in_order, 3)

        # actually sort our so-called rooms_in_order list, most recent room first
        rooms_in_order.sort(
            key=lambda r: -(notifs_by_room[r][-1]['received_ts'] or 0)
        )

        rooms = []

        for r in rooms_in_order:
            roomvars = yield self.get_room_vars(
                r, user_id, notifs_by_room[r], notif_events, state_by_room[r]
            )
            rooms.append(roomvars)

        reason['room_name'] = yield calculate_room_name(
            self.store, state_by_room[reason['room_id']], user_id,
            fallback_to_members=True
        )

        summary_text = yield self.make_summary_text(
            notifs_by_room, state_by_room, notif_events, user_id, reason
        )

        template_vars = {
            "user_display_name": user_display_name,
            "unsubscribe_link": self.make_unsubscribe_link(
                user_id, app_id, email_address
            ),
            "summary_text": summary_text,
            "app_name": self.app_name,
            "rooms": rooms,
            "reason": reason,
        }

        html_text = self.notif_template_html.render(**template_vars)
        html_part = MIMEText(html_text, "html", "utf8")

        plain_text = self.notif_template_text.render(**template_vars)
        text_part = MIMEText(plain_text, "plain", "utf8")

        multipart_msg = MIMEMultipart('alternative')
        multipart_msg['Subject'] = "[%s] %s" % (self.app_name, summary_text)
        multipart_msg['From'] = from_string
        multipart_msg['To'] = email_address
        multipart_msg['Date'] = email.utils.formatdate()
        multipart_msg['Message-ID'] = email.utils.make_msgid()
        multipart_msg.attach(text_part)
        multipart_msg.attach(html_part)

        logger.info("Sending email push notification to %s" % email_address)
        # logger.debug(html_text)

        yield sendmail(
            self.hs.config.email_smtp_host,
            raw_from, raw_to, multipart_msg.as_string(),
            port=self.hs.config.email_smtp_port
        )
Ejemplo n.º 10
0
    def make_summary_text(self, notifs_by_room, state_by_room,
                          notif_events, user_id, reason):
        if len(notifs_by_room) == 1:
            # Only one room has new stuff
            room_id = notifs_by_room.keys()[0]

            # If the room has some kind of name, use it, but we don't
            # want the generated-from-names one here otherwise we'll
            # end up with, "new message from Bob in the Bob room"
            room_name = yield calculate_room_name(
                self.store, state_by_room[room_id], user_id, fallback_to_members=False
            )

            my_member_event = state_by_room[room_id][("m.room.member", user_id)]
            if my_member_event.content["membership"] == "invite":
                inviter_member_event = state_by_room[room_id][
                    ("m.room.member", my_member_event.sender)
                ]
                inviter_name = name_from_member_event(inviter_member_event)

                if room_name is None:
                    defer.returnValue(INVITE_FROM_PERSON % {
                        "person": inviter_name,
                        "app": self.app_name
                    })
                else:
                    defer.returnValue(INVITE_FROM_PERSON_TO_ROOM % {
                        "person": inviter_name,
                        "room": room_name,
                        "app": self.app_name,
                    })

            sender_name = None
            if len(notifs_by_room[room_id]) == 1:
                # There is just the one notification, so give some detail
                event = notif_events[notifs_by_room[room_id][0]["event_id"]]
                if ("m.room.member", event.sender) in state_by_room[room_id]:
                    state_event = state_by_room[room_id][("m.room.member", event.sender)]
                    sender_name = name_from_member_event(state_event)

                if sender_name is not None and room_name is not None:
                    defer.returnValue(MESSAGE_FROM_PERSON_IN_ROOM % {
                        "person": sender_name,
                        "room": room_name,
                        "app": self.app_name,
                    })
                elif sender_name is not None:
                    defer.returnValue(MESSAGE_FROM_PERSON % {
                        "person": sender_name,
                        "app": self.app_name,
                    })
            else:
                # There's more than one notification for this room, so just
                # say there are several
                if room_name is not None:
                    defer.returnValue(MESSAGES_IN_ROOM % {
                        "room": room_name,
                        "app": self.app_name,
                    })
                else:
                    # If the room doesn't have a name, say who the messages
                    # are from explicitly to avoid, "messages in the Bob room"
                    sender_ids = list(set([
                        notif_events[n['event_id']].sender
                        for n in notifs_by_room[room_id]
                    ]))

                    defer.returnValue(MESSAGES_FROM_PERSON % {
                        "person": descriptor_from_member_events([
                            state_by_room[room_id][("m.room.member", s)]
                            for s in sender_ids
                        ]),
                        "app": self.app_name,
                    })
        else:
            # Stuff's happened in multiple different rooms

            # ...but we still refer to the 'reason' room which triggered the mail
            if reason['room_name'] is not None:
                defer.returnValue(MESSAGES_IN_ROOM_AND_OTHERS % {
                    "room": reason['room_name'],
                    "app": self.app_name,
                })
            else:
                # If the reason room doesn't have a name, say who the messages
                # are from explicitly to avoid, "messages in the Bob room"
                sender_ids = list(set([
                    notif_events[n['event_id']].sender
                    for n in notifs_by_room[reason['room_id']]
                ]))

                defer.returnValue(MESSAGES_FROM_PERSON_AND_OTHERS % {
                    "person": descriptor_from_member_events([
                        state_by_room[reason['room_id']][("m.room.member", s)]
                        for s in sender_ids
                    ]),
                    "app": self.app_name,
                })