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")
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 })
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})
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)
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
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)))
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
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")
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)
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)
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))
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
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)
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())
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
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")
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
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
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>'
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 })
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)
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.")
def prepare_channel_data(): c_model = ChannelModel(Platform=Platform.LINE, Token="ABC") ChannelManager.insert_one_model(c_model) return c_model
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")
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)
def setUpTestData(cls) -> None: MONGO_CLIENT.get_database("channel").get_collection( "dict").delete_many({}) ChannelManager.register(1, "channel1")
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")
def process_pass(self): self._result = ChannelManager.get_channel_packed( self._platform, self._channel_token)
def process_pass(self): self._result = ChannelManager.update_channel_nickname( self._channel_oid, self._root_oid, self._new_name)
def target_channel(self): from mongodb.factory import ChannelManager return ChannelManager.get_channel_oid(self.target_channel_oid)