mirror of
https://git.ugnet.gay/CrossTalk/azul.git
synced 2026-05-27 22:59:49 +00:00
587 lines
18 KiB
Python
587 lines
18 KiB
Python
import time
|
|
import struct
|
|
|
|
from array import array
|
|
from core import error
|
|
from core.models import ContactList, Substatus
|
|
from dataclasses import dataclass
|
|
from enum import IntEnum
|
|
from typing import Optional
|
|
from util.misc import Logger
|
|
|
|
from .buddy import build_presence_notif # TODO(subpurple): move build_presence_notif out of buddy?
|
|
from ..proto.tlv import TLV
|
|
from ..proto.snac import OSCARClient, OSCARContext, SNACMessage, Foodgroup, Subgroup
|
|
from ..proto.buffer import Buffer
|
|
|
|
|
|
# I only put the classes I need in this IntEnum
|
|
#
|
|
# For a complete list of feedbag classes, see https://ninawiki.preloading.dev/wiki/Protocols/OSCAR/Foodgroups/FEEDBAG/Items#Class:_FEEDBAG_CLASS_IDS
|
|
class FeedbagClass(IntEnum):
|
|
Buddy = 0x0000,
|
|
Group = 0x0001,
|
|
Permit = 0x0002,
|
|
Deny = 0x0003,
|
|
PdInfo = 0x0004,
|
|
BuddyPrefs = 0x0005
|
|
|
|
|
|
class FeedbagAttributes(IntEnum):
|
|
Order = 0x00C8,
|
|
PdMode = 0x00CA,
|
|
WirelessPdMode = 0x00D0,
|
|
WirelessIgnoreMode = 0x00D1,
|
|
FishPdMode = 0x00D2,
|
|
FishIgnoreMode = 0x00D3,
|
|
PdMask = 0x00CB,
|
|
BuddyPrefs = 0x00C9
|
|
|
|
class FeedbagStatus(IntEnum):
|
|
Success = 0x0000,
|
|
NotFound = 0x0002,
|
|
AlreadyExists = 0x0003,
|
|
BadRequest = 0x000A,
|
|
OverLimit = 0x000C,
|
|
BadLoginID = 0x0010,
|
|
|
|
@dataclass
|
|
class FeedbagItem:
|
|
name: str
|
|
group_id: int
|
|
item_id: int
|
|
class_id: int
|
|
attributes: array[TLV]
|
|
|
|
def __init__(self, name: str, group_id: int, item_id: int, class_id: int, attributes: Optional[array[TLV]] = None) -> None:
|
|
self.name = name
|
|
self.group_id = group_id
|
|
self.item_id = item_id
|
|
self.class_id = class_id
|
|
self.attributes = attributes or []
|
|
|
|
def marshal(self) -> bytes:
|
|
buf = Buffer()
|
|
buf.write_string_u16(self.name)
|
|
buf.write_u16(self.group_id)
|
|
buf.write_u16(self.item_id)
|
|
buf.write_u16(self.class_id)
|
|
buf.write_tlv_l_block(self.attributes)
|
|
|
|
return buf.data
|
|
|
|
|
|
def get_items(context: OSCARContext) -> array[FeedbagItem]:
|
|
user = context.user
|
|
detail = user.detail
|
|
|
|
contacts = list({
|
|
*detail.get_contacts_by_list(ContactList.AL),
|
|
*detail.get_contacts_by_list(ContactList.FL)
|
|
})
|
|
|
|
group_feedbag = []
|
|
group_order = b""
|
|
|
|
contact_feedbag = []
|
|
|
|
for id, group in detail._groups_by_id.items():
|
|
order = b""
|
|
|
|
for contact in contacts:
|
|
user = contact.head
|
|
|
|
for grp in contact._groups.copy():
|
|
if grp.id == id:
|
|
contact_feedbag.append(FeedbagItem(user.username, int(group.id), user.id, FeedbagClass.Buddy))
|
|
order += struct.pack(">H", user.id)
|
|
|
|
group_feedbag.append(FeedbagItem(group.name, int(group.id), 0x0000, FeedbagClass.Group, [
|
|
TLV(FeedbagAttributes.Order, order)
|
|
]))
|
|
|
|
group_order += struct.pack(">H", int(group.id))
|
|
|
|
# deal with any ungrouped contacts left
|
|
if ungrouped_contacts := [contact for contact in contacts if not contact._groups]:
|
|
no_group_gid = len(group_feedbag) + 1
|
|
|
|
order = b""
|
|
|
|
for contact in ungrouped_contacts:
|
|
user = contact.head
|
|
|
|
contact_feedbag.append(FeedbagItem(user.username, no_group_gid, user.id, FeedbagClass.Buddy))
|
|
|
|
order += struct.pack(">H", user.id)
|
|
|
|
group_feedbag.append(FeedbagItem("(No Group)", no_group_gid, 0x0000, FeedbagClass.Group, [
|
|
TLV(FeedbagAttributes.Order, order)
|
|
]))
|
|
|
|
return [
|
|
FeedbagItem("", 0x0000, 0x0000, FeedbagClass.Group, [
|
|
TLV(FeedbagAttributes.Order, group_order)
|
|
]),
|
|
|
|
FeedbagItem("", 0x0000, 0x0E63, FeedbagClass.PdInfo, [
|
|
TLV(FeedbagAttributes.PdMode, struct.pack(">H", 0x0004)),
|
|
TLV(FeedbagAttributes.WirelessPdMode, struct.pack(">B", 0x0001)),
|
|
TLV(FeedbagAttributes.WirelessIgnoreMode, struct.pack(">B", 0x0001)),
|
|
TLV(FeedbagAttributes.FishPdMode, struct.pack(">B", 0x0001)),
|
|
TLV(FeedbagAttributes.FishIgnoreMode, struct.pack(">B", 0x0001)),
|
|
TLV(FeedbagAttributes.PdMask, struct.pack(">H", 0xFFFF))
|
|
]),
|
|
|
|
FeedbagItem("", 0x0000, 0x4B1D, FeedbagClass.BuddyPrefs, [
|
|
TLV(FeedbagAttributes.BuddyPrefs, struct.pack(">L", 0x00000400))
|
|
]),
|
|
|
|
*group_feedbag,
|
|
*contact_feedbag
|
|
]
|
|
|
|
|
|
def marshal_items(items: array[FeedbagItem]) -> bytes:
|
|
return b"".join([item.marshal() for item in items])
|
|
|
|
|
|
def unmarshal_items(item_bytes: bytes) -> array[FeedbagItem]:
|
|
buf = Buffer(item_bytes)
|
|
items = []
|
|
|
|
# Minimum feedbag item size (in bytes):
|
|
# Name = +2 = 2
|
|
# Group ID = +2 = 4
|
|
# Item ID = +2 = 6
|
|
# Class ID = +2 = 8
|
|
# Attributes = +2 = 10
|
|
#
|
|
while len(buf) > 10:
|
|
name = buf.read_string_u16()
|
|
group_id = buf.read_u16()
|
|
item_id = buf.read_u16()
|
|
class_id = buf.read_u16()
|
|
attributes = buf.read_tlv_l_block()
|
|
|
|
items.append(FeedbagItem(name, group_id, item_id, class_id, attributes))
|
|
|
|
return items
|
|
|
|
|
|
def _send_feedbag_status(client: OSCARClient, request_id: int, statuses: list) -> None:
|
|
msg = SNACMessage(0x0013, 0x000E, 0x0000, request_id)
|
|
for code in statuses:
|
|
msg.write_u16(code)
|
|
client.send_snac(msg)
|
|
|
|
|
|
def _resolve_group_backend_id(context: OSCARContext, client_group_id: int) -> Optional[str]:
|
|
detail = context.user.detail
|
|
if detail is None:
|
|
return None
|
|
if str(client_group_id) in detail._groups_by_id:
|
|
return str(client_group_id)
|
|
mapping = context.bs.front_data.get("oscar0_group_id_map", {})
|
|
return mapping.get(client_group_id)
|
|
|
|
def build_buddy_added_notif(adder_username: str) -> SNACMessage:
|
|
msg = SNACMessage(0x0013, 0x001C)
|
|
username_bytes = adder_username.encode('ascii')
|
|
msg.write_u8(len(username_bytes))
|
|
msg.write_bytes(username_bytes)
|
|
return msg
|
|
|
|
@Foodgroup(0x0013)
|
|
class FeedbagFoodgroup:
|
|
logger: Logger
|
|
|
|
@Subgroup(0x0002)
|
|
def rights_query(self, client: OSCARClient, context: OSCARContext, message: SNACMessage) -> None:
|
|
self.logger.info("[Client] FEEDBAG__RIGHTS_QUERY")
|
|
|
|
response_msg = SNACMessage(0x0013, 0x0003)
|
|
|
|
# https://ninawiki.preloading.dev/wiki/Protocols/OSCAR/SNAC/FEEDBAG_RIGHTS_REPLY#TLV_Class:_FEEDBAG_RIGHTS_REPLY_TAGS
|
|
response_msg.write_tlvs([
|
|
TLV(0x0002, struct.pack(">H", 254)), # max class attrs
|
|
TLV(0x0003, struct.pack(">H", 1698)), # max item attrs
|
|
|
|
# max items
|
|
TLV(0x0004, struct.pack(f">{'H' * 67}",
|
|
1000, # max num of contacts
|
|
100, # max num of groups
|
|
1000, # max num of visible contacts
|
|
1000, # max num of invisible contacts
|
|
1, # max vis/invis bitmasks
|
|
1, # max presence info fields
|
|
150, # limit for item type 06
|
|
12, # limit for item type 07
|
|
12, # limit for item type 08
|
|
3, # limit for item type 09
|
|
50, # limit for item type 0a
|
|
50, # limit for item type 0b
|
|
0, # limit for item type 0c
|
|
128, # limit for item type 0d
|
|
1000, # max ignore list entries
|
|
20, # limit for item type 0f
|
|
200, # limit for item 10
|
|
1, # limit for item 11
|
|
100, # limit for item 12
|
|
1, # limit for item 13
|
|
25, # limit for item 14
|
|
|
|
# These values are unknown but are here in the sake of keeping response
|
|
# parity with NINA:
|
|
1, 40, 1, 10, 200, 1, 60, 200, 1, 8, 20, 1, 10000, 1000, 1000, 50, 1, 5,
|
|
500, 1, 8, 10000, 1, 1, 1, 10000, 0, 0, 1, 2000, 0, 60, 24, 10, 1, 0, 0,
|
|
0, 0, 1, 1, 1, 1, 1000, 1, 1)),
|
|
|
|
TLV(0x0005, struct.pack(">H", 100)), # max client items
|
|
TLV(0x0006, struct.pack(">H", 97)), # max item name len
|
|
TLV(0x0007, struct.pack(">H", 2000)), # max recent buddies
|
|
TLV(0x0008, struct.pack(">H", 10)), # interaction buddies
|
|
TLV(0x0009, struct.pack(">L", 432000)), # interaction half life - in 2^(-age/half_life) in seconds
|
|
TLV(0x000A, struct.pack(">L", 14)), # interaction max score
|
|
TLV(0x000B, struct.pack(">H", 0)), # unknown
|
|
TLV(0x000C, struct.pack(">H", 600)), # max buddies per group
|
|
TLV(0x000D, struct.pack(">H", 200)), # max allowed bot buddies
|
|
TLV(0x000E, struct.pack(">H", 32)) # max smart groups
|
|
])
|
|
|
|
self.logger.info("[Server] FEEDBAG__RIGHTS_REPLY")
|
|
client.send_snac(response_msg)
|
|
|
|
@Subgroup(0x0004)
|
|
def query(self, client: OSCARClient, context: OSCARContext, message: SNACMessage) -> None:
|
|
self.logger.info("[Client] FEEDBAG__QUERY")
|
|
|
|
items = get_items(context)
|
|
|
|
response_msg = SNACMessage(0x0013, 0x0006, 0x0000, message.request_id)
|
|
response_msg.write_u8(0) # feedbag protocol version - always 0
|
|
response_msg.write_u16(len(items)) # no. of feedbag items
|
|
response_msg.write_bytes(marshal_items(items)) # list of feedbag items
|
|
response_msg.write_u32(int(time.time())) # feedbag last change time - TODO: should pull from db
|
|
|
|
self.logger.info("[Server] FEEDBAG__REPLY")
|
|
client.send_snac(response_msg)
|
|
|
|
@Subgroup(0x0005)
|
|
def query_if_modified(self, client: OSCARClient, context: OSCARContext, message: SNACMessage) -> None:
|
|
self.logger.info("[Client] FEEDBAG__QUERY_IF_MODIFIED")
|
|
|
|
# 66 51 29 47 00 0D
|
|
#
|
|
# 66 51 29 47 - u32 for unix timestamp of cached client-side feedbag
|
|
# 00 0D - u16 for number of items in cached client-side feedbag
|
|
cached_feedbag_timestamp = message.read_u32()
|
|
cached_feedbag_num_items = message.read_u16()
|
|
|
|
self.logger.info("[Client] Cached feedbag timestamp:", cached_feedbag_timestamp)
|
|
self.logger.info("[Client] Cached feedbag items num:", cached_feedbag_num_items)
|
|
|
|
current_items = get_items(context)
|
|
|
|
if len(current_items) == cached_feedbag_num_items:
|
|
response_msg = SNACMessage(0x0013, 0x000F, 0x0000, message.request_id)
|
|
response_msg.write_u32(cached_feedbag_timestamp)
|
|
response_msg.write_u16(cached_feedbag_num_items)
|
|
|
|
self.logger.info("[Server] FEEDBAG__REPLY_NOT_MODIFIED")
|
|
client.send_snac(response_msg)
|
|
else:
|
|
self.query(client, context, message)
|
|
|
|
@Subgroup(0x0007)
|
|
def use(self, client: OSCARClient, context: OSCARContext, message: SNACMessage) -> None:
|
|
self.logger.info("[Client] FEEDBAG__USE")
|
|
|
|
# set our status to Online
|
|
context.bs.me_update({
|
|
"substatus": Substatus.Online
|
|
})
|
|
|
|
# notify the client if any contacts are online
|
|
user = context.user
|
|
detail = user.detail
|
|
|
|
contacts = list({
|
|
*detail.get_contacts_by_list(ContactList.AL)
|
|
})
|
|
|
|
for contact in contacts:
|
|
if not contact.status.is_offlineish():
|
|
client.send_snac(build_presence_notif(contact))
|
|
|
|
# stubs because I cba to implement more of feedbag rn
|
|
@Subgroup(0x0011)
|
|
def start_cluster(self, client: OSCARClient, context: OSCARContext, message: SNACMessage) -> None:
|
|
self.logger.info("[Client] FEEDBAG__START_CLUSTER (not implemented)")
|
|
|
|
@Subgroup(0x0012)
|
|
def end_cluster(self, client: OSCARClient, context: OSCARContext, message: SNACMessage) -> None:
|
|
self.logger.info("[Client] FEEDBAG__END_CLUSTER (not implemented)")
|
|
|
|
@Subgroup(0x0008)
|
|
def insert_item(self, client: OSCARClient, context: OSCARContext, message: SNACMessage) -> None:
|
|
self.logger.info("[Client] FEEDBAG__INSERT_ITEM")
|
|
|
|
bs = context.bs
|
|
user = context.user
|
|
detail = user.detail
|
|
items = unmarshal_items(message.data)
|
|
statuses = []
|
|
|
|
for item in items:
|
|
if item.class_id == FeedbagClass.Group:
|
|
if item.group_id == 0 and item.item_id == 0:
|
|
statuses.append(FeedbagStatus.Success)
|
|
continue
|
|
|
|
existing_names = {g.name.lower() for g in detail._groups_by_id.values()}
|
|
if item.name.lower() in existing_names:
|
|
statuses.append(FeedbagStatus.AlreadyExists)
|
|
continue
|
|
|
|
try:
|
|
group = bs.me_group_add(item.name)
|
|
mapping = bs.front_data.setdefault("oscar0_group_id_map", {})
|
|
mapping[item.group_id] = group.id
|
|
self.logger.info("[Client] Added group:", item.name)
|
|
statuses.append(FeedbagStatus.Success)
|
|
except Exception:
|
|
statuses.append(FeedbagStatus.BadRequest)
|
|
|
|
elif item.class_id == FeedbagClass.Buddy:
|
|
if not item.name:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact_uuid = context.backend.util_get_uuid_from_username(item.name)
|
|
if contact_uuid is None:
|
|
self.logger.info("[Client] Unknown buddy:", item.name)
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact = detail.contacts.get(contact_uuid)
|
|
if contact is not None and contact.lists & ContactList.FL:
|
|
statuses.append(FeedbagStatus.AlreadyExists)
|
|
continue
|
|
|
|
try:
|
|
bs.me_contact_add(contact_uuid, ContactList.FL | ContactList.AL)
|
|
|
|
if item.group_id != 0:
|
|
backend_group_id = _resolve_group_backend_id(context, item.group_id)
|
|
if backend_group_id is not None:
|
|
bs.me_group_contact_add(backend_group_id, contact_uuid)
|
|
|
|
self.logger.info("[Client] Added buddy:", item.name)
|
|
statuses.append(FeedbagStatus.Success)
|
|
except error.ContactListIsFull:
|
|
statuses.append(FeedbagStatus.OverLimit)
|
|
except Exception:
|
|
statuses.append(FeedbagStatus.BadRequest)
|
|
|
|
elif item.class_id == FeedbagClass.Deny:
|
|
if not item.name:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact_uuid = context.backend.util_get_uuid_from_username(item.name)
|
|
if contact_uuid is None:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact = detail.contacts.get(contact_uuid)
|
|
if contact is not None and contact.lists & ContactList.BL:
|
|
statuses.append(FeedbagStatus.AlreadyExists)
|
|
continue
|
|
|
|
try:
|
|
bs.me_contact_add(contact_uuid, ContactList.BL)
|
|
self.logger.info("[Client] Added deny entry:", item.name)
|
|
statuses.append(FeedbagStatus.Success)
|
|
except Exception:
|
|
statuses.append(FeedbagStatus.BadRequest)
|
|
|
|
elif item.class_id == FeedbagClass.Permit:
|
|
if not item.name:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact_uuid = context.backend.util_get_uuid_from_username(item.name)
|
|
if contact_uuid is None:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact = detail.contacts.get(contact_uuid)
|
|
if contact is not None and contact.lists & ContactList.AL:
|
|
statuses.append(FeedbagStatus.AlreadyExists)
|
|
continue
|
|
|
|
try:
|
|
bs.me_contact_add(contact_uuid, ContactList.AL)
|
|
self.logger.info("[Client] Added permit entry:", item.name)
|
|
statuses.append(FeedbagStatus.Success)
|
|
except Exception:
|
|
statuses.append(FeedbagStatus.BadRequest)
|
|
|
|
else:
|
|
statuses.append(FeedbagStatus.Success)
|
|
|
|
self.logger.info("[Server] FEEDBAG__STATUS")
|
|
_send_feedbag_status(client, message.request_id, statuses)
|
|
|
|
@Subgroup(0x0009)
|
|
def update_item(self, client: OSCARClient, context: OSCARContext, message: SNACMessage) -> None:
|
|
self.logger.info("[Client] FEEDBAG__UPDATE_ITEM")
|
|
|
|
bs = context.bs
|
|
user = context.user
|
|
detail = user.detail
|
|
items = unmarshal_items(message.data)
|
|
statuses = []
|
|
|
|
for item in items:
|
|
if item.class_id == FeedbagClass.Group:
|
|
if item.group_id == 0 and item.item_id == 0:
|
|
statuses.append(FeedbagStatus.Success)
|
|
continue
|
|
|
|
backend_group_id = _resolve_group_backend_id(context, item.group_id)
|
|
if backend_group_id is None:
|
|
statuses.append(FeedbagStatus.NotFound)
|
|
continue
|
|
|
|
group = detail._groups_by_id.get(backend_group_id)
|
|
if group is None:
|
|
statuses.append(FeedbagStatus.NotFound)
|
|
continue
|
|
|
|
if item.name and item.name != group.name:
|
|
try:
|
|
bs.me_group_edit(backend_group_id, new_name=item.name)
|
|
self.logger.info("[Client] Renamed group to:", item.name)
|
|
statuses.append(FeedbagStatus.Success)
|
|
except Exception:
|
|
statuses.append(FeedbagStatus.BadRequest)
|
|
else:
|
|
statuses.append(FeedbagStatus.Success)
|
|
|
|
else:
|
|
statuses.append(FeedbagStatus.Success)
|
|
|
|
self.logger.info("[Server] FEEDBAG__STATUS")
|
|
_send_feedbag_status(client, message.request_id, statuses)
|
|
|
|
@Subgroup(0x000A)
|
|
def delete_item(self, client: OSCARClient, context: OSCARContext, message: SNACMessage) -> None:
|
|
self.logger.info("[Client] FEEDBAG__DELETE_ITEM")
|
|
|
|
bs = context.bs
|
|
user = context.user
|
|
detail = user.detail
|
|
items = unmarshal_items(message.data)
|
|
statuses = []
|
|
|
|
for item in items:
|
|
if item.class_id == FeedbagClass.Group:
|
|
if item.group_id == 0 and item.item_id == 0:
|
|
statuses.append(FeedbagStatus.BadRequest)
|
|
continue
|
|
|
|
backend_group_id = _resolve_group_backend_id(context, item.group_id)
|
|
if backend_group_id is None:
|
|
statuses.append(FeedbagStatus.NotFound)
|
|
continue
|
|
|
|
try:
|
|
bs.me_group_remove(backend_group_id)
|
|
mapping = bs.front_data.get("oscar0_group_id_map", {})
|
|
for k in [k for k, v in mapping.items() if v == backend_group_id]:
|
|
del mapping[k]
|
|
self.logger.info("[Client] Removed group:", item.name)
|
|
statuses.append(FeedbagStatus.Success)
|
|
except Exception:
|
|
statuses.append(FeedbagStatus.NotFound)
|
|
|
|
elif item.class_id == FeedbagClass.Buddy:
|
|
if not item.name:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact_uuid = context.backend.util_get_uuid_from_username(item.name)
|
|
if contact_uuid is None:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact = detail.contacts.get(contact_uuid)
|
|
if contact is None or not (contact.lists & ContactList.FL):
|
|
statuses.append(FeedbagStatus.NotFound)
|
|
continue
|
|
|
|
try:
|
|
if item.group_id != 0:
|
|
backend_group_id = _resolve_group_backend_id(context, item.group_id)
|
|
if backend_group_id is not None:
|
|
bs.me_group_contact_remove(backend_group_id, contact_uuid)
|
|
|
|
if not contact._groups:
|
|
bs.me_contact_remove(contact_uuid, ContactList.FL)
|
|
|
|
self.logger.info("[Client] Removed buddy:", item.name)
|
|
statuses.append(FeedbagStatus.Success)
|
|
except Exception:
|
|
statuses.append(FeedbagStatus.BadRequest)
|
|
|
|
elif item.class_id == FeedbagClass.Deny:
|
|
if not item.name:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact_uuid = context.backend.util_get_uuid_from_username(item.name)
|
|
if contact_uuid is None:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact = detail.contacts.get(contact_uuid)
|
|
if contact is None or not (contact.lists & ContactList.BL):
|
|
statuses.append(FeedbagStatus.NotFound)
|
|
continue
|
|
|
|
try:
|
|
bs.me_contact_remove(contact_uuid, ContactList.BL)
|
|
self.logger.info("[Client] Removed deny entry:", item.name)
|
|
statuses.append(FeedbagStatus.Success)
|
|
except Exception:
|
|
statuses.append(FeedbagStatus.BadRequest)
|
|
|
|
elif item.class_id == FeedbagClass.Permit:
|
|
if not item.name:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact_uuid = context.backend.util_get_uuid_from_username(item.name)
|
|
if contact_uuid is None:
|
|
statuses.append(FeedbagStatus.BadLoginID)
|
|
continue
|
|
|
|
contact = detail.contacts.get(contact_uuid)
|
|
if contact is None or not (contact.lists & ContactList.AL):
|
|
statuses.append(FeedbagStatus.NotFound)
|
|
continue
|
|
|
|
try:
|
|
bs.me_contact_remove(contact_uuid, ContactList.AL)
|
|
self.logger.info("[Client] Removed permit entry:", item.name)
|
|
statuses.append(FeedbagStatus.Success)
|
|
except Exception:
|
|
statuses.append(FeedbagStatus.BadRequest)
|
|
|
|
else:
|
|
statuses.append(FeedbagStatus.Success)
|
|
|
|
self.logger.info("[Server] FEEDBAG__STATUS")
|
|
_send_feedbag_status(client, message.request_id, statuses) |