-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathLogic.lua
More file actions
381 lines (326 loc) · 15 KB
/
Logic.lua
File metadata and controls
381 lines (326 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
-- Logic.lua
-- Who lookups, Hooks, Message storage
-- Who lookups
function MessageBox:AddToWhoQueue(name)
if not MessageBox.settings.backgroundWho then return end
-- Never send WHO to Game Masters
if MessageBox.playerCache[name] and MessageBox.playerCache[name].isGM then return end
-- Already have complete data (class + guild resolved, even if guild is empty string)
if MessageBox.playerCache[name] and MessageBox.playerCache[name].class and MessageBox.playerCache[name].guild ~= nil then return end
-- Already in progresss WHO is currently running for this name
if MessageBox.playerCache[name] and MessageBox.playerCache[name].whoInProgress then return end
local nameLower = string.lower(name)
-- Check if already the current in progress WHO target
if MessageBox.currentWhoEntry and MessageBox.currentWhoEntry.nameLower == nameLower then return end
-- Check if already in the queue
for _, entry in ipairs(MessageBox.whoQueue) do
if entry.nameLower == nameLower then return end
end
-- Cap queue size
if table.getn(MessageBox.whoQueue) >= MessageBox.WHO_QUEUE_MAX then
table.remove(MessageBox.whoQueue, 1)
end
table.insert(MessageBox.whoQueue, { name = name, nameLower = nameLower, attempts = 0 })
end
function MessageBox:ProcessWhoQueue()
if not MessageBox.settings.backgroundWho then return end
local now = GetTime()
-- Timeout: if waiting too long for a WHO result, reset
if MessageBox.waitingForWhoResult then
if (now - MessageBox.waitingForWhoSince) > MessageBox.WHO_TIMEOUT then
local entry = MessageBox.currentWhoEntry
-- Clear the in progress
if entry then
if MessageBox.playerCache[entry.name] then
MessageBox.playerCache[entry.name].whoInProgress = nil
end
entry.attempts = entry.attempts + 1
-- Only requeue if under max attempts and not already cached
if entry.attempts < 3 then
local cache = MessageBox.playerCache[entry.name]
if not (cache and cache.class and cache.guild ~= nil) then
table.insert(MessageBox.whoQueue, entry)
end
end
end
MessageBox.waitingForWhoResult = false
MessageBox.currentWhoEntry = nil
MessageBox.whoTimer = now
end
return
end
if table.getn(MessageBox.whoQueue) == 0 then return end
if FriendsFrame and FriendsFrame:IsVisible() then return end
if WhoFrame and WhoFrame:IsVisible() then return end
if (now - MessageBox.whoTimer) < MessageBox.WHO_INTERVAL then return end
-- Skip entries that have already been resolved while waiting in queue
while table.getn(MessageBox.whoQueue) > 0 do
local peek = MessageBox.whoQueue[1]
local cache = MessageBox.playerCache[peek.name]
if cache and cache.class and cache.guild ~= nil then
table.remove(MessageBox.whoQueue, 1)
else
break
end
end
if table.getn(MessageBox.whoQueue) == 0 then return end
local entry = MessageBox.whoQueue[1]
table.remove(MessageBox.whoQueue, 1)
if not MessageBox.playerCache[entry.name] then
MessageBox.playerCache[entry.name] = {}
end
MessageBox.playerCache[entry.name].whoInProgress = true
MessageBox.whoTimer = now
MessageBox.waitingForWhoResult = true
MessageBox.waitingForWhoSince = now
MessageBox.currentWhoEntry = entry
SendWho("n-" .. entry.name)
end
function MessageBox:HandleWhoResult()
if not MessageBox.waitingForWhoResult then return end
local numWhos, totalCount = GetNumWhoResults()
local found = false
local targetName = MessageBox.currentWhoEntry and MessageBox.currentWhoEntry.name or nil
local targetLower = MessageBox.currentWhoEntry and MessageBox.currentWhoEntry.nameLower or nil
for i = 1, numWhos do
local name, guild, level, race, class, zone, group = GetWhoInfo(i)
if name then
if not MessageBox.playerCache[name] then MessageBox.playerCache[name] = {} end
MessageBox.playerCache[name].level = level
MessageBox.playerCache[name].class = class
MessageBox.playerCache[name].classUpper = class and string.upper(class) or nil
MessageBox.playerCache[name].race = race
MessageBox.playerCache[name].guild = guild
MessageBox.playerCache[name].zone = zone
MessageBox.playerCache[name].whoInProgress = nil
-- Persist class permanently and level if max (60)
if class and MessageBox.settings.classCache then
if not MessageBox.settings.classCache[name] then
MessageBox.settings.classCache[name] = {}
end
MessageBox.settings.classCache[name].class = class
MessageBox.settings.classCache[name].classUpper = string.upper(class)
if level and tonumber(level) == 60 then
MessageBox.settings.classCache[name].level = level
end
end
if targetLower and string.lower(name) == targetLower then
found = true
end
end
end
if not found and targetName then
-- Clear the in progress
if MessageBox.playerCache[targetName] then
MessageBox.playerCache[targetName].whoInProgress = nil
end
if numWhos == 0 then
-- Zero results: player is offline or doesn't exist — no point retrying
-- Mark guild as empty string so dont queue this name endlessly
if not MessageBox.playerCache[targetName] then
MessageBox.playerCache[targetName] = {}
end
MessageBox.playerCache[targetName].guild = MessageBox.playerCache[targetName].guild or ""
else
local entry = MessageBox.currentWhoEntry
entry.attempts = entry.attempts + 1
if entry.attempts < 2 then
table.insert(MessageBox.whoQueue, entry)
end
end
end
MessageBox.waitingForWhoResult = false
MessageBox.currentWhoEntry = nil
if targetName and MessageBox.conversations and MessageBox.conversations[targetName] then
MessageBox.conversations[targetName]._fmtCache = nil
end
if MessageBox.frame and MessageBox.frame:IsVisible() then
MessageBox:MarkContactListDirty()
if MessageBox.selectedContact then
MessageBox:UpdateChatHeader()
end
end
if targetName and MessageBox.detachedWindows[targetName] and MessageBox.detachedWindows[targetName]:IsVisible() then
if MessageBox.detachedWindows[targetName].UpdateHeader then
MessageBox.detachedWindows[targetName]:UpdateHeader()
end
end
end
function MessageBox:PrintWhoQueue()
if MessageBox.currentWhoEntry then
DEFAULT_CHAT_FRAME:AddMessage("|cff3cb7f0Message|rBox: WHO in progress: " .. MessageBox.currentWhoEntry.name .. " (Attempts: " .. MessageBox.currentWhoEntry.attempts .. ")")
end
local count = table.getn(MessageBox.whoQueue)
if count == 0 and not MessageBox.currentWhoEntry then
DEFAULT_CHAT_FRAME:AddMessage("|cff3cb7f0Message|rBox: WHO Queue is empty.")
elseif count > 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cff3cb7f0Message|rBox: WHO Queue (" .. count .. "):")
for i, entry in ipairs(MessageBox.whoQueue) do
DEFAULT_CHAT_FRAME:AddMessage(i .. ". " .. entry.name .. " (Attempts: " .. entry.attempts .. ")")
end
end
end
function MessageBox:ClearWhoQueue()
for _, entry in ipairs(MessageBox.whoQueue) do
if MessageBox.playerCache[entry.name] then
MessageBox.playerCache[entry.name].whoInProgress = nil
end
end
if MessageBox.currentWhoEntry and MessageBox.playerCache[MessageBox.currentWhoEntry.name] then
MessageBox.playerCache[MessageBox.currentWhoEntry.name].whoInProgress = nil
end
MessageBox.whoQueue = {}
MessageBox.waitingForWhoResult = false
MessageBox.currentWhoEntry = nil
DEFAULT_CHAT_FRAME:AddMessage("|cff3cb7f0Message|rBox: WHO Queue cleared.")
end
-- Hooks
function MessageBox:SetupHooks()
if not self.original_ChatFrame_SendTell then
self.original_ChatFrame_SendTell = ChatFrame_SendTell
ChatFrame_SendTell = function(name, chatFrame)
if MessageBox.settings.interceptWhispers then
MessageBox:SelectContact(name)
MessageBox:ShowFrame()
return
end
MessageBox.original_ChatFrame_SendTell(name, chatFrame)
end
end
if not self.original_ChatFrame_OnEvent then
self.original_ChatFrame_OnEvent = ChatFrame_OnEvent
ChatFrame_OnEvent = function(event)
if MessageBox.settings.suppressWhispers then
if event == "CHAT_MSG_WHISPER" or event == "CHAT_MSG_WHISPER_INFORM" then
return
end
end
MessageBox.original_ChatFrame_OnEvent(event)
end
end
if not self.original_ChatEdit_ParseText then
self.original_ChatEdit_ParseText = ChatEdit_ParseText
ChatEdit_ParseText = function(editBox, send)
if MessageBox.settings.interceptWhispers and send == 0 then
local text = editBox:GetText()
if text and string.len(text) > 0 then
local s, e, cmd, target = string.find(text, "^/([wWtT]%S*)%s+([^%s]+)%s*$")
if s then
cmd = string.lower(cmd)
if cmd == "w" or cmd == "t" or cmd == "whisper" or cmd == "tell" then
editBox:SetText("")
editBox:Hide()
MessageBox:SelectContact(target)
MessageBox:ShowFrame()
return
end
end
end
end
return MessageBox.original_ChatEdit_ParseText(editBox, send)
end
end
end
-- Message storage
function MessageBox:AddMessage(contact, message, isOutgoing)
if not self.conversations then return end
if not self.conversations[contact] then
self.conversations[contact] = self:NewConversation()
end
local c = self.conversations[contact]
table.insert(c.messages, message)
table.insert(c.times, time())
table.insert(c.outgoing, isOutgoing)
table.insert(c.system, false)
c.count = (c.count or 0) + 1
MessageBox.conversationOrderDirty = true
-- Mark crash save as needing a flush; throttled immediate flush
MessageBox.crashSaveDirty = true
if MessageBox.hasNampower then
local now = GetTime()
if (now - MessageBox.lastFlushTime) >= MessageBox.FLUSH_MIN_INTERVAL then
MessageBox:FlushToDisk()
end
end
-- Update popouts
if self.detachedWindows[contact] and self.detachedWindows[contact]:IsVisible() then
local win = self.detachedWindows[contact]
win.scrollBar:SetMinMaxValues(1, c.count)
win.scrollBar:SetValue(c.count)
end
if not isOutgoing then
local detachedOpen = (self.detachedWindows[contact] and self.detachedWindows[contact]:IsVisible())
local mainOpen = (self.frame and self.frame:IsVisible() and self.selectedContact == contact)
if not mainOpen and not detachedOpen then
if not self.unreadCounts[contact] then
self.unreadCounts[contact] = 0
end
self.unreadCounts[contact] = self.unreadCounts[contact] + 1
self:UpdateMinimapBadge()
if self.settings.popupNotificationsEnabled and (not self.frame or not self.frame:IsVisible()) then
self:ShowNotificationPopup()
end
end
end
if self.frame and self.frame:IsVisible() then
self:MarkContactListDirty()
if self.selectedContact == contact then
local timeFmt = self.settings.use12HourFormat and "%I:%M %p" or "%H:%M"
local lastIdx = c.count
local timeString = "|cff808080[" .. date(timeFmt, c.times[lastIdx]) .. "]|r"
local nameColor = isOutgoing and "|cff8080ff" or "|cffff80ff"
local name = isOutgoing and "You" or self.selectedContact
local cleanMessage = self:HandleLink(message)
local formattedMessage = string.format("%s %s%s:|r %s%s|r", timeString, nameColor, name, "|cffffffff", cleanMessage)
self.chatHistory:AddMessage(formattedMessage)
self.chatHistory:ScrollToBottom()
self:UpdateChatHeader()
end
end
end
function MessageBox:AddSystemMessage(contact, message, isTransient)
if not self.conversations[contact] then
self.conversations[contact] = self:NewConversation()
end
local c = self.conversations[contact]
table.insert(c.messages, message)
table.insert(c.times, time())
table.insert(c.outgoing, false)
table.insert(c.system, true)
c.count = (c.count or 0) + 1
-- New message changes sort order
MessageBox.conversationOrderDirty = true
-- Mark crash save as needing a flush
MessageBox.crashSaveDirty = true
if self.detachedWindows[contact] and self.detachedWindows[contact]:IsVisible() then
local win = self.detachedWindows[contact]
win.scrollBar:SetMinMaxValues(1, c.count)
win.scrollBar:SetValue(c.count)
end
if self.frame and self.frame:IsVisible() and self.selectedContact == contact then
local timeFmt = self.settings.use12HourFormat and "%I:%M %p" or "%H:%M"
local lastIdx = c.count
local timeString = "|cff808080[" .. date(timeFmt, c.times[lastIdx]) .. "]|r"
local formattedMessage = string.format("%s %s%s|r", timeString, "|cffffcc00", message)
self.chatHistory:AddMessage(formattedMessage)
self.chatHistory:ScrollToBottom()
if self.chatScrollBar then
self.chatScrollBar.isUpdating = true
self.chatScrollBar:SetMinMaxValues(1, c.count)
self.chatScrollBar:SetValue(c.count)
self.chatScrollBar.isUpdating = false
end
end
end
function MessageBox:RemoveLastMessage(contact)
if not self.conversations or not self.conversations[contact] then return end
local c = self.conversations[contact]
local count = c.count or table.getn(c.messages)
if count > 0 then
table.remove(c.messages)
table.remove(c.times)
table.remove(c.outgoing)
table.remove(c.system)
c.count = count - 1
end
end