Exemplo n.º 1
0
    def get_room_vars(self, room_id, user_id, notifs, notif_events,
                      room_state):
        my_member_event = room_state[("m.room.member", user_id)]
        is_invite = my_member_event.content["membership"] == "invite"

        room_vars = {
            "title": calculate_room_name(room_state, user_id),
            "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)

                # 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 = 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)
Exemplo n.º 2
0
    def get_room_vars(self, room_id, user_id, notifs, notif_events, room_state):
        my_member_event = room_state[("m.room.member", user_id)]
        is_invite = my_member_event.content["membership"] == "invite"

        room_vars = {
            "title": calculate_room_name(room_state, user_id),
            "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
                )

                # 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 = 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)
Exemplo n.º 3
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(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'] = calculate_room_name(
            state_by_room[reason['room_id']], user_id, fallback_to_members=True
        )

        summary_text = 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
        )
Exemplo n.º 4
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 = calculate_room_name(
                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:
                    return INVITE_FROM_PERSON % {
                        "person": inviter_name,
                        "app": self.app_name
                    }
                else:
                    return 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:
                    return MESSAGE_FROM_PERSON_IN_ROOM % {
                        "person": sender_name,
                        "room": room_name,
                        "app": self.app_name,
                    }
                elif sender_name is not None:
                    return 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:
                    return 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]
                    ]))

                    return 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:
                return 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']]
                ]))

                return 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,
                }
Exemplo n.º 5
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 = calculate_room_name(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:
                    return INVITE_FROM_PERSON % {
                        "person": inviter_name,
                        "app": self.app_name
                    }
                else:
                    return 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:
                    return MESSAGE_FROM_PERSON_IN_ROOM % {
                        "person": sender_name,
                        "room": room_name,
                        "app": self.app_name,
                    }
                elif sender_name is not None:
                    return 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:
                    return 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]
                        ]))

                    return 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:
                return 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']]
                    ]))

                return 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,
                }
Exemplo n.º 6
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(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'] = calculate_room_name(
            state_by_room[reason['room_id']],
            user_id,
            fallback_to_members=True)

        summary_text = 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)