annotate Squawk.lua @ 14:18c33a32ba31

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