channel_controller.py (12150B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\chat\client\channel_controller.py 2 import blue 3 import sys 4 import signals 5 import localization 6 import gametime 7 import threadutils 8 import logging 9 from chat.client.hide_messages import open_hide_message_settings 10 from collections import deque 11 from carbon.common.script.util.commonutils import StripTags 12 from carbon.common.script.util.format import FmtDate 13 from carbonui.util.stringManip import SanitizeFilename 14 from eveProto.generated.eve_public.chat.local.local_pb2 import CLASSIFICATION_INVISIBLE 15 from inventorycommon.const import ownerSystem, ownerNone 16 from chatutil import CompleteAutoLinks 17 from chat.common.const import CLASSIFACTION_TO_COLOR, MemberClassification 18 from chat.client.const import MAX_MESSAGES_IN_HISTORY, CHATLOG_TEMPLATE, MAX_MESSAGES_PER_SEC, MAX_PENDING_MESSAGES 19 from chat.client.util import parse_chat_message, should_hide_message 20 from player_messaging.client.ui_message import prompt_player_fsd_dialog 21 logger = logging.getLogger('chat') 22 23 class BaseChatChannelController(object): 24 __notifyevents__ = [] 25 26 def __init__(self, channel_id, category_id): 27 self._is_closing = False 28 self._channel_id = channel_id 29 self._category_id = category_id 30 self._window_id = 'chatchannel_%s' % self._channel_id 31 self._member_ids = set() 32 self._pending_messages = set() 33 self._messages = deque(maxlen=MAX_MESSAGES_IN_HISTORY) 34 self._sent_message_timestamps = deque(maxlen=MAX_MESSAGES_PER_SEC) 35 self._logfile = None 36 self._muted = None 37 self.on_members_set = signals.Signal('on_members_set') 38 self.on_members_cleared = signals.Signal('on_members_cleared') 39 self.on_member_added = signals.Signal('on_member_added') 40 self.on_member_removed = signals.Signal('on_member_removed') 41 self.on_member_data_changed = signals.Signal('on_member_data_changed') 42 self.on_message_added = signals.Signal('on_message_added') 43 self.on_messages_cleared = signals.Signal('on_messages_cleared') 44 self.on_pending_message_removed = signals.Signal('on_pending_message_removed') 45 self._register() 46 47 def close(self): 48 if self._is_closing: 49 return 50 self._is_closing = True 51 self._pending_messages.clear() 52 self._messages.clear() 53 self._sent_message_timestamps.clear() 54 self._unregister() 55 56 def _register(self): 57 sm.RegisterNotify(self) 58 self.chat_service.on_member_data_changed.connect(self._on_member_data_changed) 59 60 def _unregister(self): 61 sm.UnregisterNotify(self) 62 self.chat_service.on_member_data_changed.disconnect(self._on_member_data_changed) 63 64 @property 65 def chat_service(self): 66 return sm.GetService('chat') 67 68 @property 69 def channel_id(self): 70 return self._channel_id 71 72 @property 73 def category_id(self): 74 return self._category_id 75 76 @property 77 def window_id(self): 78 return self._window_id 79 80 @property 81 def window_display_name(self): 82 return self.category_id 83 84 @property 85 def is_closable(self): 86 return False 87 88 @property 89 def member_ids(self): 90 return self._member_ids 91 92 @property 93 def member_count(self): 94 return len(self._member_ids) 95 96 @property 97 def member_count_tooltip(self): 98 return localization.GetByLabel('UI/Chat/CapsuleerCounterHint') 99 100 @property 101 def messages(self): 102 return self._messages 103 104 @property 105 def message_count(self): 106 return len(self._messages) 107 108 @property 109 def motd(self): 110 return '' 111 112 def close_by_user(self): 113 self.chat_service.close_channel(self.channel_id) 114 115 def clear_messages(self): 116 if not self._messages: 117 return 118 self._messages.clear() 119 self._pending_messages.clear() 120 self._sent_message_timestamps.clear() 121 self.on_messages_cleared() 122 123 def _clear_members(self): 124 if not self._member_ids: 125 return 126 self._member_ids.clear() 127 self.on_members_cleared() 128 129 def _on_member_data_changed(self, character_id, data): 130 if character_id in self._member_ids: 131 self.on_member_data_changed(character_id, data) 132 133 def message_received(self, sender_id, text, message_id = None): 134 if sender_id == session.charid: 135 return 136 if self.chat_service.is_blocked_character(sender_id, self.get_member_data(sender_id)): 137 return 138 if self.chat_service.is_reported_spammer(sender_id): 139 return 140 text = parse_chat_message(text) 141 if not text: 142 return 143 if self.should_hide_message(text): 144 return 145 self._add_to_messages(sender_id, text) 146 147 def should_hide_message(self, text): 148 return should_hide_message(self.channel_id, self.category_id, text) 149 150 def open_hide_message_settings(self): 151 open_hide_message_settings(category_id=self.category_id, channel_id=self.channel_id, channel_name=self.channel_id) 152 153 def add_game_message(self, text, sender_id = None): 154 self._add_to_messages(sender_id, text) 155 156 def can_send_chat_message(self): 157 if self._is_at_sent_message_cap(): 158 prompt_player_fsd_dialog('uiwarning03') 159 return False 160 if self._muted: 161 self.add_game_message(self._get_muted_reason(), sender_id=ownerSystem) 162 return False 163 return True 164 165 def _is_at_sent_message_cap(self): 166 if len(self._pending_messages) > MAX_PENDING_MESSAGES: 167 return True 168 if len(self._sent_message_timestamps) < MAX_MESSAGES_PER_SEC: 169 return False 170 return gametime.GetSecondsSinceWallclockTime(self._sent_message_timestamps[0]) < 1 171 172 def send_chat_message(self, text): 173 pass 174 175 def _prime_unprimed_members(self, member_ids): 176 members_to_prime = set() 177 for member_id in member_ids: 178 if not cfg.eveowners.GetIfExists(member_id): 179 members_to_prime.add(member_id) 180 181 cfg.eveowners.Prime(members_to_prime) 182 183 def set_members(self, members): 184 if not members: 185 self._clear_members() 186 return 187 members = {member_id:member_data for member_id, member_data in members.iteritems() if member_data['classification'] != CLASSIFICATION_INVISIBLE} 188 self._member_ids = set(members.keys()) 189 self._prime_unprimed_members(self._member_ids) 190 self.chat_service.update_members_info(members) 191 self.on_members_set() 192 193 def add_member(self, character_id, character_info = None): 194 if character_id in self._member_ids: 195 return 196 if character_info and character_info['classification'] == CLASSIFICATION_INVISIBLE: 197 return 198 self._member_ids.add(character_id) 199 if character_info: 200 self.chat_service.update_members_info({character_id: character_info}) 201 self.on_member_added(character_id) 202 203 def remove_member(self, character_id): 204 if character_id not in self._member_ids: 205 return 206 self._member_ids.remove(character_id) 207 self.on_member_removed(character_id) 208 209 def can_invite_character(self, character_id): 210 return False 211 212 def invite_character(self, character_id): 213 pass 214 215 def get_member_data(self, member_id): 216 return self.chat_service.get_member_info(member_id) 217 218 def has_member_data(self, member_id): 219 return self.chat_service.has_member_info(member_id) 220 221 def get_color_for_sender(self, member_id): 222 if member_id in (ownerSystem, ownerNone): 223 return '0xb2ee6666' 224 if self.has_member_data(member_id): 225 sender_classification = self.get_member_data(member_id).get('classification', None) 226 return CLASSIFACTION_TO_COLOR.get(sender_classification, '0x99ffffff') 227 try: 228 name = cfg.eveowners.Get(member_id).name 229 if name.startswith('GM'): 230 return CLASSIFACTION_TO_COLOR[MemberClassification.GAME_MASTER] 231 if name.startswith('CCP'): 232 return CLASSIFACTION_TO_COLOR[MemberClassification.DEVELOPER] 233 if name.startswith('ISD'): 234 return CLASSIFACTION_TO_COLOR[MemberClassification.VOLUNTEER] 235 return '0x99ffffff' 236 except: 237 return '0x99ffffff' 238 239 def set_muted(self, end_date, reason): 240 self._muted = {'end_date': end_date, 241 'reason': reason} 242 243 def clear_muted(self): 244 self._muted = None 245 246 def mute_player(self, character_id, duration, reason): 247 pass 248 249 def _get_muted_reason(self): 250 if not self._muted: 251 return '' 252 return self._muted['reason'] 253 254 def _add_to_messages(self, sender_id, text, timestamp = None, color = None): 255 if not timestamp: 256 timestamp = gametime.GetWallclockTime() 257 if not color: 258 color = self.get_color_for_sender(sender_id) 259 text = CompleteAutoLinks(text) 260 self._messages.append((sender_id, 261 text, 262 timestamp, 263 color)) 264 self.on_message_added(sender_id, text, timestamp, color) 265 self._write_to_log_file(sender_id, text, timestamp) 266 267 def _add_pending_message(self, message_id): 268 self._pending_messages.add(message_id) 269 self._sent_message_timestamps.append(gametime.GetWallclockTime()) 270 271 def _remove_pending_message(self, message_id, was_delivered = True): 272 if message_id not in self._pending_messages: 273 return 274 self._pending_messages.remove(message_id) 275 self.on_pending_message_removed(message_id, was_delivered) 276 277 def _close_logfile(self): 278 if self._logfile: 279 self._logfile.Close() 280 self._logfile = None 281 282 @threadutils.threaded 283 def _setup_log_file(self): 284 if self._logfile or not settings.user.ui.Get('logchat', True): 285 return 286 now = gametime.GetWallclockTime() 287 if session.charid: 288 character_name = cfg.eveowners.Get(session.charid).name 289 else: 290 logger.warning('_SetupLogfile %s: No charid in session. Listener will be unknown character in chatlog' % self.window_display_name) 291 character_name = 'unknown_character' 292 chatlog = CHATLOG_TEMPLATE % (self.channel_id, 293 self.window_display_name, 294 character_name, 295 FmtDate(now)) 296 year, month, weekday, day, hour, minute, second, msec = blue.os.GetTimeParts(now) 297 date_time = '%d%.2d%.2d_%.2d%.2d%.2d' % (year, 298 month, 299 day, 300 hour, 301 minute, 302 second) 303 filename = '%s_%s' % (self.window_display_name, date_time) 304 filename = SanitizeFilename(filename) 305 post_fix = '_%s' % session.charid if session.charid else '' 306 filename = blue.sysinfo.GetUserDocumentsDirectory() + '/EVE/logs/Chatlogs/%s%s.txt' % (filename, post_fix) 307 try: 308 self._logfile = blue.classes.CreateInstance('blue.ResFile') 309 if not self._logfile.Open(filename, 0): 310 self._logfile.Create(filename) 311 self._logfile.Write(chatlog.encode('utf-16')) 312 except Exception: 313 self._logfile = None 314 logger.exception('Failed to instantiate log file') 315 sys.exc_clear() 316 317 @threadutils.threaded 318 def _write_to_log_file(self, sender_id, text, timestamp): 319 if self._logfile is None or self._logfile.size <= 0: 320 return 321 text_without_tags = StripTags(text).replace('<', '<').replace('>', '>').replace('&', '&') 322 try: 323 if sender_id is None: 324 who = localization.GetByLabel('UI/Common/Message') 325 else: 326 who = cfg.eveowners.Get(int(sender_id)).name 327 except: 328 who = sender_id 329 330 line = '[%20s ] %s > %s\r\n' % (FmtDate(timestamp), who, text_without_tags) 331 try: 332 self._logfile.Write(line.encode('utf-16')) 333 except: 334 logger.error('Failed to write chat message to logfile', sender_id, text)