Ejemplo n.º 1
0
    def setUpTestData(cls) -> None:
        from mongodb.factory import RootUserManager
        from extutils.gidentity import GoogleIdentityUserData

        MONGO_CLIENT.get_database("execode").get_collection(
            "main").delete_many({})
        MONGO_CLIENT.get_database("channel").get_collection(
            "dict").delete_many({})
        MONGO_CLIENT.get_database("channel").get_collection(
            "user").delete_many({})
        MONGO_CLIENT.get_database("channel").get_collection(
            "perm").delete_many({})
        MONGO_CLIENT.get_database("user").get_collection("api").delete_many({})
        MONGO_CLIENT.get_database("user").get_collection("root").delete_many(
            {})

        # Register Fake API Data
        reg = RootUserManager.register_google(
            GoogleIdentityUserData("Fake",
                                   "Fake",
                                   "Fake",
                                   "*****@*****.**",
                                   skip_check=True))
        if reg.success:
            cls.TEST_API_TOKEN = reg.idt_reg_result.model.token
        else:
            raise ValueError("Fake data registration failed.")

        ChannelManager.register(1, "channel1")
Ejemplo n.º 2
0
def handle_join(request, event, destination):
    ChannelManager.register(Platform.LINE, LineApiUtils.get_channel_id(event))
    LINE.temp_apply_format(event_dest_fmt,
                           logging.INFO,
                           "Bot joined a group.",
                           extra={
                               ExtraKey.Event: event,
                               ExtraKey.Destination: destination
                           })
Ejemplo n.º 3
0
    def get(self, request, *args, **kwargs):
        # `kwargs` will be used as `nav_param` so extract channel_oid from `kwargs` instead of creating param.
        channel_oid_str = kwargs.get("channel_oid", "")
        channel_oid = safe_cast(channel_oid_str, ObjectId)
        u_profs = ProfileManager.get_user_profiles(channel_oid, get_root_oid(request))

        if u_profs:
            return render_template(
                self.request, _("Channel Management - {}").format(channel_oid), "account/channel/manage.html", {
                    "user_profiles": u_profs,
                    "perm_sum": sorted(ProfileManager.get_permissions(u_profs), key=lambda x: x.code),
                    "channel_oid": channel_oid
                }, nav_param=kwargs)
        else:
            c_prof = ChannelManager.get_channel_oid(channel_oid)

            if c_prof:
                messages.info(
                    request, _("You are redirected to the channel info page "
                               "because you don't have any connections linked to the channel."),
                    extra_tags="info"
                )

                return redirect(reverse("info.channel", kwargs={"channel_oid": channel_oid}))
            else:
                return WebsiteErrorView.website_error(
                    request, WebsiteError.PROFILE_LINK_NOT_FOUND, {"channel_oid": channel_oid_str})
Ejemplo n.º 4
0
def get_channel_data(kwargs) -> ChannelDataGetResult:
    channel_oid_str = kwargs.get("channel_oid", "")
    channel_oid = safe_cast(channel_oid_str, ObjectId)

    model = ChannelManager.get_channel_oid(channel_oid)

    return ChannelDataGetResult(ok=model is not None, model=model, oid_org=channel_oid_str)
Ejemplo n.º 5
0
    def get_channel_profiles(
            channel_oid: ObjectId,
            partial_name: Optional[str] = None) -> List[ChannelProfileEntry]:
        ret = []

        # Get channel profiles. Terminate if no available profiles
        profs = list(
            ProfileManager.get_channel_profiles(channel_oid, partial_name))
        if not profs:
            return ret

        # Get channel data. Terminate if no channel data found
        channel_model = ChannelManager.get_channel_oid(channel_oid)
        if not channel_model:
            return ret

        # Get user names, and the prof-channel dict
        user_oids_dict = ProfileManager.get_profiles_user_oids(
            [prof.id for prof in profs])
        user_oids = []
        for k, v in user_oids_dict.items():
            user_oids.extend(v)
        user_names = IdentitySearcher.get_batch_user_name(user_oids,
                                                          channel_model,
                                                          on_not_found=None)

        for prof in profs:
            uids = user_oids_dict.get(prof.id, [])

            ret.append(
                ChannelProfileEntry(prof,
                                    [user_names.get(uid) for uid in uids]))

        return ret
Ejemplo n.º 6
0
def handle_member_join(request, event, destination):
    cdata = ChannelManager.get_channel_token(
        Platform.LINE, LineApiUtils.get_channel_id(event), auto_register=True)
    joined_names = []

    for user in event.joined.members:
        uid = user.user_id

        udata_result = RootUserManager.get_root_data_onplat(Platform.LINE, uid)

        if udata_result.success and cdata:
            ProfileManager.register_new_default_async(cdata.id,
                                                      udata_result.model.id)

            uname = RootUserManager.get_root_data_uname(
                udata_result.model.get_oid(), cdata).user_name
            if uname:
                joined_names.append(uname)

    LINE.temp_apply_format(event_dest_fmt,
                           logging.INFO,
                           "LINE Join Group.",
                           extra={
                               ExtraKey.Event: event,
                               ExtraKey.Destination: destination
                           })

    LineApiWrapper.reply_text(
        event.reply_token,
        _("{} joined the group.").format(" & ".join(joined_names)))
Ejemplo n.º 7
0
    def collate_child_channel_data(root_oid: ObjectId, child_channel_oids: List[ObjectId]) \
            -> List[CollatedChannelData]:
        accessible: List[CollatedChannelData] = []
        inaccessible: List[CollatedChannelData] = []

        missing_oids = []

        for ccoid in child_channel_oids:
            cdata = ChannelManager.get_channel_oid(ccoid)

            if cdata:
                ccd = CollatedChannelData(
                    channel_name=cdata.get_channel_name(root_oid),
                    channel_data=cdata)

                if cdata.bot_accessible:
                    accessible.append(ccd)
                else:
                    inaccessible.append(ccd)
            else:
                missing_oids.append(ccoid)

        if missing_oids:
            MailSender.send_email_async(
                f"No associated channel data found of the channel IDs below:<br>"
                f"<pre>{' / '.join([str(oid) for oid in missing_oids])}</pre>")

        accessible = sorted(accessible,
                            key=lambda data: data.channel_data.id,
                            reverse=True)
        inaccessible = sorted(inaccessible,
                              key=lambda data: data.channel_data.id,
                              reverse=True)

        return accessible + inaccessible
Ejemplo n.º 8
0
 async def on_private_channel_update(self, before: GroupChannel, after: GroupChannel):
     if str(before) != str(after) \
             and not ChannelManager.update_channel_default_name(Platform.DISCORD, after.id, str(after)).is_success:
         warn_txt = f"Private Channel Name UPDATED but the name was not updated. Channel token: {after.id} / " \
                    f"Name: {str(before)} to {str(after)}"
         DISCORD.logger.warning(warn_txt)
         MailSender.send_email_async(warn_txt, subject="Discord private channel name update failed")
Ejemplo n.º 9
0
    def get_default_profile(self, channel_oid: ObjectId) -> GetPermissionProfileResult:
        """
        Automatically creates a default profile for `channel_oid` if not exists.
        """
        ex = None

        cnl = ChannelManager.get_channel_oid(channel_oid)
        if not cnl:
            return GetPermissionProfileResult(GetOutcome.X_CHANNEL_NOT_FOUND, None, ex)

        try:
            prof_oid = cnl.config.default_profile_oid
        except AttributeError:
            return GetPermissionProfileResult(GetOutcome.X_CHANNEL_CONFIG_ERROR, None, ex)

        if not cnl.config.is_field_none("DefaultProfileOid"):
            perm_prof = self.find_one_casted({OID_KEY: prof_oid}, parse_cls=ChannelProfileModel)

            if perm_prof:
                return GetPermissionProfileResult(GetOutcome.O_CACHE_DB, perm_prof, ex)

        create_result = self.create_default_profile(channel_oid)

        return GetPermissionProfileResult(
            GetOutcome.O_ADDED if create_result.success else GetOutcome.X_DEFAULT_PROFILE_ERROR,
            create_result.model, ex)
Ejemplo n.º 10
0
    def get(self, request, **kwargs):
        profile_result = get_profile_data(kwargs)

        if not profile_result.ok:
            return WebsiteErrorView.website_error(
                request, WebsiteError.PROFILE_NOT_FOUND,
                {"profile_oid": profile_result.oid_org})

        root_oid = get_root_oid(request)
        profile_model = profile_result.model

        channel_model = ChannelManager.get_channel_oid(
            profile_model.channel_oid)
        permissions = ProfileManager.get_user_permissions(
            channel_model.id, root_oid)

        # noinspection PyTypeChecker
        return render_template(
            request,
            _("Profile Info - {}").format(profile_model.name),
            "info/profile.html", {
                "profile_data":
                profile_model,
                "profile_controls":
                ProfileHelper.get_user_profile_controls(
                    channel_model, profile_model.id, root_oid, permissions),
                "perm_cats":
                list(ProfilePermission),
                "is_default":
                profile_model.id == channel_model.config.default_profile_oid
            },
            nav_param=kwargs)
Ejemplo n.º 11
0
    async def on_member_remove(self, member: Member):
        udata_result = RootUserManager.get_root_data_onplat(Platform.DISCORD, member.id, auto_register=True)
        cdata = ChannelManager.get_channel_token(Platform.DISCORD, member.guild.id, auto_register=True)

        if udata_result.success and cdata:
            ProfileManager.mark_unavailable_async(cdata.id, udata_result.model.id)

        sys_channel = member.guild.system_channel
        if sys_channel:
            await sys_channel.send(_("{} left the server.").format(member.mention))
Ejemplo n.º 12
0
    def _ensure_channel_(platform: Platform, token: Union[int, str], default_name: str = None) \
            -> Optional[ChannelModel]:
        ret = ChannelManager.register(platform, token, default_name=default_name)
        if ret.success:
            # Use Thread so no need to wait until the update is completed
            Thread(target=ChannelManager.mark_accessibility, args=(platform, token, True)).start()
        else:
            MailSender.send_email_async(f"Platform: {platform} / Token: {token}",
                                        subject="Channel Registration Failed")

        return ret.model
Ejemplo n.º 13
0
    def create_default_profile(self, channel_oid: ObjectId) -> CreateProfileResult:
        default_profile, outcome, ex = self._create_profile_(channel_oid, Name=_("Default Profile"))

        if outcome.is_inserted:
            set_success = ChannelManager.set_config(
                channel_oid, ChannelConfigModel.DefaultProfileOid.key, default_profile.id)

            if not set_success:
                outcome = WriteOutcome.X_ON_SET_CONFIG

        return CreateProfileResult(outcome, default_profile, ex)
Ejemplo n.º 14
0
    def get_attachable_profiles(self, channel_oid: ObjectId, root_uid: ObjectId) -> List[ChannelProfileModel]:
        profiles = self.get_user_profiles(channel_oid, root_uid)
        exist_perm = self.get_permissions(profiles)
        highest_perm = self.get_highest_permission_level(profiles)
        attachables = {prof.id: prof
                       for prof in self._prof.get_attachable_profiles(channel_oid, exist_perm, highest_perm)}

        # Remove default profile
        channel_data = ChannelManager.get_channel_oid(channel_oid)
        del attachables[channel_data.config.default_profile_oid]

        return list(attachables.values())
Ejemplo n.º 15
0
    def search_channel(
            keyword: str,
            root_oid: Optional[ObjectId] = None) -> List[ChannelData]:
        """The keyword could be
        - A piece of the message comes from a channel
        - The name of the channel"""
        checked_choid: Set[ObjectId] = set()
        ret: List[ChannelData] = []
        missing = []

        for ch_model in ChannelManager.get_channel_default_name(
                keyword, hide_private=True):
            checked_choid.add(ch_model.id)
            ret.append(
                ChannelData(ch_model, ch_model.get_channel_name(root_oid)))

        for channel_oid in MessageRecordStatisticsManager.get_messages_distinct_channel(
                keyword):
            if channel_oid not in checked_choid:
                checked_choid.add(channel_oid)
                ch_model = ChannelManager.get_channel_oid(channel_oid,
                                                          hide_private=True)

                if ch_model:
                    ret.append(
                        ChannelData(ch_model,
                                    ch_model.get_channel_name(root_oid)))
                else:
                    missing.append(channel_oid)

        if missing:
            MailSender.send_email_async(
                f"Channel OID have no corresponding channel data in message record.\n"
                f"{' | '.join([str(i) for i in missing])}",
                subject="Missing Channel Data")

        return ret
Ejemplo n.º 16
0
    async def on_guild_channel_create(self, channel: Union[TextChannel, VoiceChannel, CategoryChannel]):
        if channel.type == ChannelType.text:
            reg_result = ChannelManager.register(Platform.DISCORD, channel.id, channel_full_repr(channel))

            if not reg_result.success:
                warn_txt = f"Guild Channel CREATED but the registration was failed. Channel token: {channel.id}"
                DISCORD.logger.warning(warn_txt)
                MailSender.send_email_async(warn_txt, subject="Discord guild channel creation failed")

            if not ChannelCollectionManager.register(
                    Platform.DISCORD, channel.guild.id, reg_result.model.id, str(channel.guild)).success:
                warn_txt = f"Guild Channel CREATED but the collection registration was failed. " \
                           f"Channel token: {channel.id}"
                DISCORD.logger.warning(warn_txt)
                MailSender.send_email_async(warn_txt, subject="Discord channel collection creation failed")
Ejemplo n.º 17
0
    def _excde_ar_add_(action_model: ExecodeEntryModel, xparams: dict) -> ExecodeCompletionOutcome:
        cnl = ChannelManager.register(xparams[param.AutoReply.PLATFORM], xparams[param.AutoReply.CHANNEL_TOKEN])
        if not cnl.success:
            return ExecodeCompletionOutcome.X_AR_REGISTER_CHANNEL

        try:
            conn = AutoReplyModuleExecodeModel(**action_model.data, from_db=True).to_actual_model(
                cnl.model.id, action_model.creator_oid)
        except Exception:
            return ExecodeCompletionOutcome.X_MODEL_CONSTRUCTION

        if not AutoReplyManager.add_conn_by_model(conn).success:
            return ExecodeCompletionOutcome.X_AR_REGISTER_MODULE

        return ExecodeCompletionOutcome.O_OK
Ejemplo n.º 18
0
    def _excde_register_channel_(action_model: ExecodeEntryModel, xparams: dict) -> ExecodeCompletionOutcome:
        try:
            channel_data = ChannelManager.register(
                xparams[param.Execode.PLATFORM], xparams[param.Execode.CHANNEL_TOKEN])
        except Exception:
            return ExecodeCompletionOutcome.X_IDT_CHANNEL_ERROR

        if channel_data:
            try:
                ProfileManager.register_new_default(channel_data.model.id, action_model.creator_oid)
            except Exception:
                return ExecodeCompletionOutcome.X_IDT_REGISTER_DEFAULT_PROFILE
        else:
            return ExecodeCompletionOutcome.X_IDT_CHANNEL_NOT_FOUND

        return ExecodeCompletionOutcome.O_OK
Ejemplo n.º 19
0
    def _trans_ar_search_(model: ExtraContentModel) -> str:
        from mongodb.factory import ChannelManager, AutoReplyManager
        from mongodb.helper import IdentitySearcher

        # Early termination for falsy or not-an-array content
        if not model.content or not isinstance(model.content, list):
            return ""

        tab_list: List[str] = []
        tab_content: List[str] = []

        module_list = list(AutoReplyManager.get_conn_list_oids(model.content))

        uids = []
        for module in module_list:
            uids.append(module.creator_oid)
            if not module.active and module.remover_oid:
                uids.append(module.remover_oid)

        username_dict = {}
        if model.channel_oid:
            username_dict = IdentitySearcher.get_batch_user_name(
                uids, ChannelManager.get_channel_oid(model.channel_oid))

        for module in module_list:
            common_key = f"msg-{id(module)}"
            content = loader.render_to_string("ar/module-card.html", {
                "username_dict": username_dict,
                "module": module
            })

            tab_list.append(
                f'<a class="list-group-item list-group-item-action" '
                f'id="list-{common_key}" '
                f'data-toggle="list" href="#{common_key}" role="tab">{module.keyword.content_html}</a>'
            )
            tab_content.append(
                f'<div class="tab-pane fade" id="{common_key}" role="tabpanel" '
                f'aria-labelledby="list-{common_key}">{content}</div>')

        return f'<div class="row">' \
               f'<div class="col-4"><div class="list-group" id="list-tab" role="tablist">{"".join(tab_list)}' \
               f'</div></div>' \
               f'<div class="col-8"><div class="tab-content" id="nav-tabContent">{"".join(tab_content)}</div>' \
               f'</div></div>'
Ejemplo n.º 20
0
def handle_member_left(request, event, destination):
    for user in event.left.members:
        uid = user.user_id

        udata_result = RootUserManager.get_root_data_onplat(Platform.LINE, uid)
        cdata = ChannelManager.get_channel_token(
            Platform.LINE,
            LineApiUtils.get_channel_id(event),
            auto_register=True)

        if udata_result.success and cdata:
            ProfileManager.mark_unavailable_async(cdata.id,
                                                  udata_result.model.id)

    LINE.temp_apply_format(event_dest_fmt,
                           logging.INFO,
                           "LINE Left Group.",
                           extra={
                               ExtraKey.Event: event,
                               ExtraKey.Destination: destination
                           })
Ejemplo n.º 21
0
    def post(self, request, **kwargs):
        sender_oid = get_root_oid(request)

        profile_result = get_profile_data(kwargs)

        if not profile_result.ok:
            return HttpResponse(status=404)

        channel_model = ChannelManager.get_channel_oid(
            profile_result.model.channel_oid)

        # --- Get form data

        action = InfoPageActionControl.parse(request.POST.get("action"))
        target_uid = safe_cast(request.POST.get("uid"), ObjectId)

        if not action.is_argument_valid(target_uid):
            return HttpResponse(status=400)

        # --- Check permission

        permissions = ProfileManager.get_user_permissions(
            channel_model.id, sender_oid)

        # --- Execute corresponding action

        profile_oid = profile_result.model.id

        if action == InfoPageActionControl.DETACH:
            return InfoPageActionControl.action_detach(request,
                                                       channel_model.id,
                                                       sender_oid, target_uid,
                                                       permissions,
                                                       profile_oid)
        elif action == InfoPageActionControl.DELETE:
            return InfoPageActionControl.action_delete(request, channel_model,
                                                       profile_oid)
        else:
            return HttpResponse(status=501)
Ejemplo n.º 22
0
    def fn():
        marked_unavailable = 0

        dict_onplat_oids = RootUserManager.get_root_to_onplat_dict()
        dict_onplat_data = RootUserManager.get_onplat_data_dict()
        dict_channel = ChannelManager.get_channel_dict([p.channel_oid for p in list_prof_conn], accessbible_only=True)

        with ThreadPoolExecutor(max_workers=4, thread_name_prefix="ExstCheck") as executor:
            futures = [
                executor.submit(_check_on_prof_conn_, d, set_name_to_cache, dict_onplat_oids, dict_onplat_data,
                                dict_channel)
                for d in list_prof_conn
            ]

            # Non-lock call & Free resources when execution is done
            executor.shutdown(False)

            for completed in futures:
                ret = completed.result()
                if ret:
                    marked_unavailable += 1

        SYSTEM.logger.info(f"Marked {marked_unavailable} connections unavailable.")
Ejemplo n.º 23
0
    def prepare_channel_data():
        c_model = ChannelModel(Platform=Platform.LINE, Token="ABC")
        ChannelManager.insert_one_model(c_model)

        return c_model
Ejemplo n.º 24
0
 async def on_guild_channel_delete(self, channel: Union[TextChannel, VoiceChannel, CategoryChannel]):
     if channel.type == ChannelType.text \
             and not ChannelManager.deregister(Platform.DISCORD, channel.id).is_success:
         warn_txt = f"Guild Channel DELETED but the deregistration was failed. Channel token: {channel.id}"
         DISCORD.logger.warning(warn_txt)
         MailSender.send_email_async(warn_txt, subject="Discord guild channel deletion failed")
Ejemplo n.º 25
0
    def get_user_channel_profiles(self, root_uid: Optional[ObjectId], inside_only: bool = True,
                                  accessbible_only: bool = True) \
            -> List[ChannelProfileListEntry]:
        if root_uid is None:
            return []

        ret = []

        not_found_channel = []
        not_found_prof_oids_dict = {}

        channel_oid_list = []
        profile_oid_list = []
        prof_conns = []
        for d in self._conn.get_user_channel_profiles(root_uid, inside_only):
            channel_oid_list.append(d.channel_oid)
            profile_oid_list.extend(d.profile_oids)
            prof_conns.append(d)

        channel_dict = ChannelManager.get_channel_dict(channel_oid_list, accessbible_only=False)
        profile_dict = self._prof.get_profile_dict(profile_oid_list)

        for prof_conn in prof_conns:
            not_found_prof_oids = []

            # Get Channel Model
            cnl_oid = prof_conn.channel_oid
            cnl = channel_dict.get(cnl_oid)

            if cnl is None:
                not_found_channel.append(cnl_oid)
                continue
            elif accessbible_only and not cnl.bot_accessible:
                continue

            default_profile_oid = cnl.config.default_profile_oid

            # Get Profile Model
            prof = []
            for p in prof_conn.profile_oids:
                pm = profile_dict.get(p)
                if pm:
                    prof.append(pm)
                else:
                    not_found_prof_oids.append(p)

            if len(not_found_prof_oids) > 0:
                # There's some profile not found in the database while ID is registered
                not_found_prof_oids_dict[cnl_oid] = not_found_prof_oids

            perms = self.get_permissions(prof)
            can_ced_profile = self.can_ced_profile(perms)

            ret.append(
                ChannelProfileListEntry(
                    channel_data=cnl, channel_name=cnl.get_channel_name(root_uid), profiles=prof,
                    starred=prof_conn.starred, default_profile_oid=default_profile_oid,
                    can_ced_profile=can_ced_profile
                ))

        if len(not_found_channel) > 0 or len(not_found_prof_oids_dict) > 0:
            not_found_prof_oids_txt = "\n".join(
                [f'{cnl_id}: {" / ".join([str(oid) for oid in prof_ids])}'
                 for cnl_id, prof_ids in not_found_prof_oids_dict.items()])

            MailSender.send_email_async(
                f"User ID: <code>{root_uid}</code><hr>"
                f"Channel IDs not found in DB:<br>"
                f"<pre>{' & '.join([str(c) for c in not_found_channel])}</pre><hr>"
                f"Profile IDs not found in DB:<br>"
                f"<pre>{not_found_prof_oids_txt}</pre>",
                subject="Possible Data Corruption on Getting User Profile Connection"
            )

        return sorted(ret, key=lambda item: item.channel_data.bot_accessible, reverse=True)
Ejemplo n.º 26
0
 def setUpTestData(cls) -> None:
     MONGO_CLIENT.get_database("channel").get_collection(
         "dict").delete_many({})
     ChannelManager.register(1, "channel1")
Ejemplo n.º 27
0
 async def on_private_channel_create(self, channel: Union[DMChannel, GroupChannel]):
     if not ChannelManager.register(Platform.DISCORD, channel.id, str(channel)).success:
         warn_txt = f"Private Channel CREATED but the registration was failed. Channel token: {channel.id}"
         DISCORD.logger.warning(warn_txt)
         MailSender.send_email_async(warn_txt, subject="Discord private channel creation failed")
Ejemplo n.º 28
0
 def process_pass(self):
     self._result = ChannelManager.get_channel_packed(
         self._platform, self._channel_token)
Ejemplo n.º 29
0
 def process_pass(self):
     self._result = ChannelManager.update_channel_nickname(
         self._channel_oid, self._root_oid, self._new_name)
Ejemplo n.º 30
0
    def target_channel(self):
        from mongodb.factory import ChannelManager

        return ChannelManager.get_channel_oid(self.target_channel_oid)