wobin@0
|
1 -- A Twitter client of sorts for World of Warcraft
|
wobin@0
|
2 -- Author: Wobin
|
wobin@0
|
3 -- Email: wobster@gmail.com
|
wobin@0
|
4 --
|
wobin@1
|
5 Squawk = LibStub("AceAddon-3.0"):NewAddon("Squawk")
|
wobin@0
|
6
|
wobin@0
|
7 Squawk.Model = {}
|
wobin@0
|
8 Squawk.View = {}
|
wobin@0
|
9 Squawk.Controller = {}
|
wobin@0
|
10
|
wobin@0
|
11 local Model = Squawk.Model
|
wobin@0
|
12 local View = Squawk.View
|
wobin@0
|
13 local Controller = Squawk.Controller
|
wobin@0
|
14
|
wobin@0
|
15 Model.UserSettings = {}
|
wobin@0
|
16 local Settings = Model.UserSettings
|
wobin@0
|
17
|
wobin@0
|
18 local defaults = {
|
wobin@0
|
19 profile = {
|
wobin@0
|
20 Squawks = {},
|
wobin@0
|
21 Follower = {},
|
wobin@0
|
22 Following = {},
|
wobin@7
|
23 Pending = {},
|
wobin@7
|
24 Requested = {},
|
wobin@0
|
25 Blocked = {},
|
wobin@0
|
26 }
|
wobin@0
|
27 }
|
wobin@0
|
28
|
wobin@0
|
29 function Squawk:OnInitialize()
|
wobin@0
|
30 Model.db = LibStub("AceDB-3.0"):New("SquawkDB", defaults)
|
wobin@4
|
31 Model.Squawks = Model.db.profile.Squawks
|
wobin@0
|
32 Settings.Follower = Model.db.profile.Follower
|
wobin@0
|
33 Settings.Following = Model.db.profile.Following
|
wobin@7
|
34 Settings.Pending = Model.db.profile.Pending
|
wobin@7
|
35 Settings.Requested = Model.db.profile.Requested
|
wobin@0
|
36 Settings.Blocked = Model.db.profile.Blocked
|
wobin@0
|
37 Settings.Private = Model.db.profile.Private
|
wobin@0
|
38
|
wobin@0
|
39 LibStub("AceComm-3.0"):Embed(Controller)
|
wobin@3
|
40 Controller:RegisterComm("Squawk", Controller.ReceiveMessage)
|
wobin@3
|
41
|
wobin@3
|
42 LibStub("AceConsole-3.0"):Embed(View)
|
wobin@0
|
43 end
|
wobin@0
|
44
|
wobin@0
|
45 -- Model --
|
wobin@0
|
46 --[[
|
wobin@0
|
47 --Each Squawk will have the following information:
|
wobin@0
|
48 -- * Owner (Name)
|
wobin@0
|
49 -- * Time (Epoch)
|
wobin@0
|
50 -- * Message (140 characters)
|
wobin@0
|
51 -- * ReplyTo (Name)
|
wobin@0
|
52 -- * Related (Names)
|
wobin@0
|
53 --
|
wobin@0
|
54 -- Each User will have the following lists:
|
wobin@0
|
55 -- * Follower
|
wobin@0
|
56 -- * Following
|
wobin@0
|
57 -- * Blocked
|
wobin@7
|
58 -- * Pending (Requests to follow that you haven't acted on)
|
wobin@7
|
59 -- * Requested (Requests to follow that you have made)
|
wobin@0
|
60 -- * Privacy State
|
wobin@7
|
61 --
|
wobin@7
|
62 -- A user can only request to follow an online person. Requests can be approved
|
wobin@7
|
63 -- on or offline, but the initial request must be made online.
|
wobin@7
|
64 --
|
wobin@7
|
65 -- When a user makes a request to follow a private user, the subsequent paths occur:
|
wobin@7
|
66 -- - Followee is added to Settings.Requested
|
wobin@7
|
67 -- - Followee receives 'follow request' -> (their) Settings.Pending
|
wobin@7
|
68 -- - Followee acts on request -> (their) Settings.Pending cleared
|
wobin@7
|
69 -- 1) Follwer is online
|
wobin@7
|
70 -- - Follower receives 'request accepted' -> Added to Settings.Following and
|
wobin@7
|
71 -- cleared from Settings.Requested
|
wobin@7
|
72 -- 2) Follower is offline
|
wobin@7
|
73 -- - The next time Follower is online and recieves a Squawk we check if there
|
wobin@7
|
74 -- is a Settings.Requested for that name, and if so assume they have approved
|
wobin@7
|
75 -- and clear/add records appropriately.
|
wobin@13
|
76 --
|
wobin@13
|
77 -- For updating, there can be a few methods.
|
wobin@13
|
78 --
|
wobin@13
|
79 -- Guild open: you're not private, you broadcast to the guild your last X
|
wobin@13
|
80 -- squawks on login
|
wobin@13
|
81 --
|
wobin@13
|
82 -- followers:
|
wobin@0
|
83 --]]
|
wobin@0
|
84 Model.Squawk = {}
|
wobin@0
|
85 local Squawk = Model.Squawk
|
wobin@13
|
86 local function wrap(str, limit)
|
wobin@13
|
87 limit = limit or 72
|
wobin@13
|
88 local here = 1
|
wobin@13
|
89 return str:gsub("(%s+)()(%S+)()",
|
wobin@13
|
90 function(sp, st, word, fi)
|
wobin@13
|
91 if fi-here > limit then
|
wobin@13
|
92 here = st
|
wobin@13
|
93 return "\n"..word
|
wobin@13
|
94 end
|
wobin@13
|
95 end)
|
wobin@13
|
96 end
|
wobin@0
|
97 function Squawk:new(Message, Owner)
|
wobin@0
|
98 local o = {}
|
wobin@0
|
99 o.Owner = Owner or UnitName("player")
|
wobin@13
|
100 o.Message = wrap(Message)
|
wobin@10
|
101 o.Time = time()
|
wobin@0
|
102 local reply, to = strsplit("@", ((strsplit(" ", Message))))
|
wobin@0
|
103 if reply == "" then
|
wobin@0
|
104 o.ReplyTo = to
|
wobin@0
|
105 end
|
wobin@0
|
106
|
wobin@0
|
107 o.Related = {}
|
wobin@0
|
108
|
wobin@0
|
109 for word in string.gmatch(Message, "@(%a+)") do
|
wobin@0
|
110 if word ~= o.ReplyTo or "" then
|
wobin@0
|
111 table.insert(o.Related, word)
|
wobin@0
|
112 end
|
wobin@0
|
113 end
|
wobin@0
|
114
|
wobin@0
|
115 return o
|
wobin@0
|
116 end
|
wobin@0
|
117
|
wobin@0
|
118 function Squawk:Sort(Squawks)
|
wobin@0
|
119 return table.sort(Squawks, function(a,b) return a.Time < b.Time end)
|
wobin@0
|
120 end
|
wobin@0
|
121
|
wobin@2
|
122 function Squawk:GetOwn(Squawks)
|
wobin@2
|
123 local mine = {}
|
wobin@2
|
124 for _, squawk in ipairs(Squawks) do
|
wobin@2
|
125 if squawk.Owner == UnitName("player") then
|
wobin@2
|
126 table.insert(mine, squawk)
|
wobin@2
|
127 end
|
wobin@2
|
128 end
|
wobin@2
|
129 return self:Sort(mine)
|
wobin@2
|
130 end
|
wobin@2
|
131
|
wobin@0
|
132 function Settings:IsPrivate()
|
wobin@0
|
133 return Settings.Private
|
wobin@0
|
134 end
|
wobin@0
|
135
|
wobin@10
|
136 function Settings:TogglePrivate()
|
wobin@10
|
137 Settings.Private = not Settings.Private
|
wobin@10
|
138 end
|
wobin@10
|
139
|
wobin@0
|
140 function Settings:AddFollower(Name)
|
wobin@2
|
141 Settings.Follower[Name] = 1
|
wobin@7
|
142 self:RemovePending(Name)
|
wobin@0
|
143 end
|
wobin@0
|
144
|
wobin@0
|
145 function Settings:AddFollowing(Name)
|
wobin@2
|
146 Settings.Following[Name] = 1
|
wobin@7
|
147 self:RemoveRequested(Name)
|
wobin@0
|
148 end
|
wobin@0
|
149
|
wobin@0
|
150 function Settings:AddBlock(Name)
|
wobin@2
|
151 Settings.Blocked[Name] = 1
|
wobin@7
|
152 self:RemoveFollower(Name)
|
wobin@7
|
153 self:RemoveFollowing(Name)
|
wobin@7
|
154 end
|
wobin@7
|
155
|
wobin@7
|
156 function Settings:AddPending(Name)
|
wobin@7
|
157 Settings.Pending[Name] = 1
|
wobin@7
|
158 end
|
wobin@7
|
159
|
wobin@7
|
160 function Settings:AddRequested(Name)
|
wobin@7
|
161 Settings.Requested[Name] = 1
|
wobin@0
|
162 end
|
wobin@0
|
163
|
wobin@0
|
164 function Settings:RemoveFollower(Name)
|
wobin@2
|
165 if Settings.Follower[Name] then
|
wobin@2
|
166 Settings.Follower[Name] = nil
|
wobin@0
|
167 end
|
wobin@0
|
168 end
|
wobin@0
|
169
|
wobin@0
|
170 function Settings:RemoveFollowing(Name)
|
wobin@2
|
171 if Settings.Following[Name] then
|
wobin@2
|
172 Settings.Following[Name] = nil
|
wobin@0
|
173 end
|
wobin@0
|
174 end
|
wobin@0
|
175
|
wobin@0
|
176 function Settings:RemoveBlock(Name)
|
wobin@2
|
177 if Settings.Blocked[Name] then
|
wobin@2
|
178 Settings.Blocked[Name] = nil
|
wobin@0
|
179 end
|
wobin@0
|
180 end
|
wobin@0
|
181
|
wobin@7
|
182 function Settings:RemovePending(Name)
|
wobin@7
|
183 if Settings.Pending[Name] then
|
wobin@7
|
184 Settings.Pending[Name] = nil
|
wobin@7
|
185 end
|
wobin@7
|
186 end
|
wobin@7
|
187
|
wobin@7
|
188 function Settings:RemoveRequested(Name)
|
wobin@7
|
189 if Settings.Requested[Name] then
|
wobin@7
|
190 Settings.Requested[Name] = nil
|
wobin@7
|
191 end
|
wobin@7
|
192 end
|
wobin@7
|
193
|
wobin@0
|
194 --Controller--
|
wobin@0
|
195
|
wobin@0
|
196 function Controller:TheyWantToFollowMe(Name)
|
wobin@0
|
197 if Settings:IsPrivate() then
|
wobin@7
|
198 Settings:AddPending(Name)
|
wobin@0
|
199 self:PutForwardFollowRequest(Name)
|
wobin@0
|
200 self:SendMessageToTarget(Name, "#Pending|"..UnitName("player"))
|
wobin@0
|
201 else
|
wobin@0
|
202 Settings:AddFollower(Name)
|
wobin@0
|
203 View:NotifyOfNewFollower(Name)
|
wobin@0
|
204 self:SendMessageToTarget(Name, "#Follow|"..UnitName("player"))
|
wobin@0
|
205 end
|
wobin@0
|
206 end
|
wobin@0
|
207
|
wobin@7
|
208 function Controller:TheyWantToUnfollowMe(Name)
|
wobin@7
|
209 Settings:RemoveFollower(Name)
|
wobin@7
|
210 end
|
wobin@7
|
211
|
wobin@0
|
212 function Controller:IWantToFollowThem(Name)
|
wobin@2
|
213 self:SendMessageToTarget(Name, "#Request|"..UnitName("player"))
|
wobin@7
|
214 Settings:AddRequested(Name)
|
wobin@7
|
215 end
|
wobin@7
|
216
|
wobin@7
|
217 function Controller:IWantToUnfollowThem(Name)
|
wobin@7
|
218 Settings:RemoveFollowing(Name)
|
wobin@7
|
219 self:SendMessageToTarget(Name, "#Unfollow|"..UnitName("player"))
|
wobin@7
|
220 View:NotifyOfUnfollowing(Name)
|
wobin@0
|
221 end
|
wobin@0
|
222
|
wobin@0
|
223 function Controller:IAmNowFollowingThem(Name)
|
wobin@0
|
224 Settings:AddFollowing(Name)
|
wobin@0
|
225 View:NotifyOfNewFollowing(Name)
|
wobin@0
|
226 end
|
wobin@0
|
227
|
wobin@7
|
228 function Controller:AddANewSquawk(Name, Message, Source)
|
wobin@10
|
229 if not Settings.Blocked[Name] then
|
wobin@7
|
230
|
wobin@7
|
231 if Source == "WHISPER" then
|
wobin@10
|
232 if Settings.Requested[Name] then -- We've been approved offline!
|
wobin@10
|
233 Settings:AddFollowing(Name)
|
wobin@7
|
234 end
|
wobin@7
|
235
|
wobin@10
|
236 if not Settings.Following[Name] then -- If we're no longer following this person
|
wobin@7
|
237 self:SendMessageToTarget(Name, "#Unfollow|"..UnitName("player"))
|
wobin@9
|
238 return
|
wobin@9
|
239 end
|
wobin@10
|
240 end
|
wobin@10
|
241
|
wobin@10
|
242 if Source == "GUILD" and Name == UnitName("player") then
|
wobin@10
|
243 return
|
wobin@7
|
244 end
|
wobin@10
|
245
|
wobin@10
|
246 table.insert(Model.Squawks, Squawk:new(Message, Name))
|
wobin@10
|
247 View:UpdateSquawkList()
|
wobin@7
|
248 end
|
wobin@0
|
249 end
|
wobin@0
|
250
|
wobin@2
|
251 function Controller:SendNewSquawk(Message)
|
wobin@10
|
252 if not Settings:IsPrivate() then
|
wobin@2
|
253 self:SendMessageToGuild("#Squawk|"..UnitName("player").."|"..Message)
|
wobin@2
|
254 end
|
wobin@2
|
255
|
wobin@2
|
256 self:AddANewSquawk(UnitName("player"), Message)
|
wobin@2
|
257
|
wobin@10
|
258 for name, _ in pairs(Settings.Following) do
|
wobin@2
|
259 self:SendMessageToTarget(name, "#Squawk|"..UnitName("player").."|"..Message)
|
wobin@2
|
260 end
|
wobin@2
|
261 end
|
wobin@2
|
262
|
wobin@1
|
263 function Controller:ImPending(Name)
|
wobin@1
|
264 View:NotifyOfPending(Name)
|
wobin@1
|
265 end
|
wobin@1
|
266
|
wobin@7
|
267 function Controller:PutForwardFollowRequest(Name)
|
wobin@7
|
268 View:NotifyOfPendingRequest(Name)
|
wobin@7
|
269 end
|
wobin@7
|
270
|
wobin@10
|
271 function Controller:ApprovePendingRequest(Name)
|
wobin@10
|
272 Settings:AddFollower(Name)
|
wobin@10
|
273 View:NotifyOfNewFollower(Name)
|
wobin@10
|
274 self:SendMessageToTarget(Name, "#Follow|"..UnitName("player"))
|
wobin@10
|
275 end
|
wobin@10
|
276
|
wobin@0
|
277 function Controller:SendMessageToTarget(Name, Message)
|
wobin@10
|
278 self:SendCommMessage("Squawk", Message, "WHISPER", Name)
|
wobin@0
|
279 end
|
wobin@0
|
280
|
wobin@0
|
281 function Controller:SendMessageToGuild(Message)
|
wobin@0
|
282 self:SendCommMessage("Squawk", Message, "GUILD")
|
wobin@0
|
283 end
|
wobin@0
|
284
|
wobin@0
|
285 local Parse = {
|
wobin@1
|
286 ["#Pending"] = Controller.ImPending,
|
wobin@1
|
287 ["#Follow"] = Controller.IAmNowFollowingThem,
|
wobin@7
|
288 ["#Unfollow"] = Controller.TheyWantToUnfollowMe,
|
wobin@2
|
289 ["#Squawk"] = Controller.AddANewSquawk,
|
wobin@2
|
290 ["#Request"] = Controller.TheyWantToFollowMe,
|
wobin@0
|
291 }
|
wobin@0
|
292
|
wobin@10
|
293 function Controller:ReceiveMessage(Message, Distribution, Sender)
|
wobin@0
|
294 local command, name, info = strsplit("|",Message)
|
wobin@10
|
295 View:Print(Distribution..":"..Message)
|
wobin@10
|
296 Parse[command](Controller, name, info, Distribution)
|
wobin@0
|
297 end
|
wobin@0
|
298 -- View --
|
wobin@0
|
299
|
wobin@0
|
300 function View:UpdateSquawkList()
|
wobin@3
|
301 self:Print("Updated Squawk List")
|
wobin@4
|
302 self:ShowMeMySquawks()
|
wobin@0
|
303 end
|
wobin@0
|
304
|
wobin@3
|
305 function View:NotifyOfPending(Name)
|
wobin@3
|
306 self:Print(Name.." will have to approve your request")
|
wobin@0
|
307 end
|
wobin@0
|
308
|
wobin@7
|
309 function View:NotifyOfPendingRequest(Name)
|
wobin@7
|
310 self:Print(Name.." wants to follow you.")
|
wobin@7
|
311 end
|
wobin@7
|
312
|
wobin@0
|
313 function View:NotifyOfNewFollowing(Name)
|
wobin@3
|
314 self:Print("You are now following "..Name)
|
wobin@0
|
315 end
|
wobin@0
|
316
|
wobin@7
|
317 function View:NotifyOfUnfollowing(Name)
|
wobin@7
|
318 self:Print("You are no longer following "..Name)
|
wobin@7
|
319 end
|
wobin@7
|
320
|
wobin@0
|
321 function View:NotifyOfNewFollower(Name)
|
wobin@3
|
322 self:Print(Name.." is now following you")
|
wobin@0
|
323 end
|
wobin@4
|
324
|
wobin@4
|
325 function View:ShowMeMySquawks()
|
wobin@5
|
326 for _,squawk in ipairs(Model.Squawks) do
|
wobin@4
|
327 self:Print(squawk.Message)
|
wobin@4
|
328 end
|
wobin@4
|
329 end
|
wobin@4
|
330
|
wobin@4
|
331 function View:ShowMeMyFollowers()
|
wobin@4
|
332 self:Print("My followers are:")
|
wobin@4
|
333 for name,_ in pairs(Settings.Follower) do
|
wobin@4
|
334 self:Print(name)
|
wobin@4
|
335 end
|
wobin@4
|
336 end
|
wobin@4
|
337
|
wobin@4
|
338 function View:ShowMeWhoImFollowing()
|
wobin@4
|
339 self:Print("I am following:")
|
wobin@4
|
340 for name,_ in pairs(Settings.Following) do
|
wobin@4
|
341 self:Print(name)
|
wobin@4
|
342 end
|
wobin@4
|
343 end
|
wobin@4
|
344
|
wobin@4
|
345 function View:ShowMeWhoIveBlocked()
|
wobin@4
|
346 self:Print("I've blocked:")
|
wobin@6
|
347 for name,_ in pairs(Settings.Blocked) do
|
wobin@4
|
348 self:Print(name)
|
wobin@4
|
349 end
|
wobin@4
|
350 end
|
wobin@10
|
351
|
wobin@13
|
352 local TimeSpan = { [1] = {"second", 60, 1},
|
wobin@13
|
353 [2] = {"minute", 3600, 60},
|
wobin@13
|
354 [3] = {"hour", 86400, 3600} }
|
wobin@13
|
355
|
wobin@13
|
356 function View:GetTime(stime)
|
wobin@13
|
357 local lapsed = difftime(time(), stime)
|
wobin@13
|
358 if lapsed < 86400 then -- if we're still in the same day...
|
wobin@13
|
359 for _,span in ipairs(TimeSpan) do
|
wobin@13
|
360 if lapsed < span[2] then
|
wobin@13
|
361 local timespan = math.floor(lapsed/span[3])
|
wobin@13
|
362 if timespan == 1 then
|
wobin@13
|
363 timespan = timespan .." ".. span[1]
|
wobin@13
|
364 else
|
wobin@13
|
365 timespan = timespan .. " ".. span[1].."s"
|
wobin@13
|
366 end
|
wobin@13
|
367 return timespan.. " ago"
|
wobin@13
|
368 end
|
wobin@13
|
369 end
|
wobin@13
|
370 end
|
wobin@13
|
371 return date("%I:%M %p %b %d", stime)
|
wobin@13
|
372 end
|
wobin@13
|
373
|
wobin@10
|
374 local LDBFeed = LibStub("LibDataBroker-1.1"):NewDataObject("Squawk", {type = "data source", text = "Awk!"})
|
wobin@10
|
375 local QTip = LibStub("LibQTip-1.0")
|
wobin@10
|
376
|
wobin@13
|
377 local function AddLine(tooltip, Line, Number, Owner, TimeStamp)
|
wobin@13
|
378 if #Line < 79 then
|
wobin@13
|
379 tooltip:AddLine(Number, Owner, Line, TimeStamp)
|
wobin@13
|
380 else
|
wobin@13
|
381 tooltip:AddLine(Number, Owner, Line:sub(1, 80).."-", TimeStamp)
|
wobin@13
|
382 AddLine(tooltip, Line:sub(81))
|
wobin@13
|
383 end
|
wobin@13
|
384 end
|
wobin@13
|
385
|
wobin@10
|
386 function LDBFeed:OnEnter()
|
wobin@13
|
387 local tooltip = QTip:Acquire("Squawk",4, "LEFT", "LEFT", "LEFT", "RIGHT")
|
wobin@10
|
388 self.tooltip = tooltip
|
wobin@13
|
389 tooltip:AddHeader('Squawk')
|
wobin@13
|
390 for i,squawk in ipairs(Model.Squawks) do
|
wobin@13
|
391 if #squawk.Message > 79 then
|
wobin@13
|
392 local head = true
|
wobin@13
|
393 local message = {strsplit("\n",squawk.Message)}
|
wobin@13
|
394 for _,line in ipairs(message) do
|
wobin@13
|
395 if head then
|
wobin@13
|
396 AddLine(tooltip, line, i..".", squawk.Owner, View:GetTime(squawk.Time))
|
wobin@13
|
397 head = false
|
wobin@13
|
398 else
|
wobin@13
|
399 AddLine(tooltip, line)
|
wobin@13
|
400 end
|
wobin@13
|
401 end
|
wobin@13
|
402 else
|
wobin@13
|
403 tooltip:AddLine(i..".", squawk.Owner, squawk.Message, View:GetTime(squawk.Time))
|
wobin@13
|
404 end
|
wobin@13
|
405 end
|
wobin@10
|
406 tooltip:SmartAnchorTo(self)
|
wobin@10
|
407 tooltip:Show()
|
wobin@10
|
408 end
|
wobin@10
|
409
|
wobin@10
|
410 function LDBFeed:OnTooltipShow()
|
wobin@10
|
411 end
|
wobin@10
|
412
|
wobin@10
|
413 function LDBFeed:OnLeave()
|
wobin@10
|
414 QTip:Release(self.tooltip)
|
wobin@10
|
415 end
|