Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- function Initialize()
- -- SET UPDATE DIVIDER
- SKIN:Bang('!SetOption', SELF:GetName(), 'UpdateDivider', -1)
- -- This script never needs to update on a schedule. It should only
- -- update when it gets a "Refresh" command from WebParser.
- -- CREATE MAIN DATABASE
- Feeds = {}
- -- CREATE TYPE MATCHING PATTERNS AND FORMATTING FUNCTIONS
- DefineTypes()
- -- GET MEASURE NAMES
- local AllMeasureNames = SELF:GetOption('MeasureName', '')
- for MeasureName in AllMeasureNames:gmatch('[^%|]+') do
- table.insert(Feeds, {
- Measure = SKIN:GetMeasure(MeasureName),
- MeasureName = MeasureName,
- Raw = nil,
- Type = nil,
- Title = nil,
- Link = nil,
- Error = nil
- })
- end
- -- MODULES
- EventFile_Initialize()
- HistoryFile_Initialize()
- -- SET STARTING FEED
- f = f or 1
- -- SET USER INPUT
- UserInput = false
- -- Used to detect when an item has been marked as read.
- end
- function Update()
- Input()
- return Output()
- end
- -----------------------------------------------------------------------
- -- INPUT
- function compareTime(a,b)
- return a.Date < b.Date
- end
- function Input(a)
- local f = a or f
- local Raw = Feeds[f].Measure:GetStringValue()
- if Raw == '' then
- Feeds[f].Error = {
- Description = 'Waiting for data from WebParser.',
- Title = 'Loading...',
- Link = 'http://enigma.kaelri.com/support'
- }
- return false
- elseif (Raw ~= Feeds[f].Raw) or UserInput then
- Feeds[f].Raw = Raw
- -- DETERMINE FEED FORMAT AND CONTENTS
- local t = IdentifyType(Raw)
- if not t then
- Feeds[f].Error = {
- Description = 'Could not identify a valid feed format.',
- Title = 'Invalid Feed Format',
- Link = 'http://enigma.kaelri.com/support'
- }
- return false
- else
- Feeds[f].Type = t
- end
- -- MAKE SYNTAX PRETTIER
- local Type = Types[t]
- -- GET NEW DATA
- Feeds[f].Title = Raw:match('<title.->(.-)</title>') or 'Untitled'
- Feeds[f].Link = Raw:match(Type.MatchLink) or nil
- local Items = {}
- for RawItem in Raw:gmatch(Type.MatchItem) do
- local Item = {}
- -- MATCH RAW DATA
- Item.Unread = 1
- if (t == 'GoogleCalendar') then
- Item.Title = RawItem:match('SUMMARY:(.-)\n') or nil
- if (string.len(Item.Title) == 1) then
- Item.Title = RawItem:match('DESCRIPTION:(.-)\n') or nil
- end
- Item.Title = Item.Title:gsub('\\', '')
- else
- Item.Title = RawItem:match('<title.->(.-)</title>') or nil
- end
- Item.Date = RawItem:match(Type.MatchItemDate) or nil
- Item.Link = RawItem:match(Type.MatchItemLink) or nil
- Item.Desc = RawItem:match(Type.MatchItemDesc) or nil
- Item.ID = RawItem:match(Type.MatchItemID) or Item.Link or Item.Title or Item.Desc or Item.Date
- -- ADDITIONAL PARSING
- if (not Item.Title) or (Item.Title == '') then
- Item.Title = 'Untitled'
- end
- if Item.Desc then
- Item.Desc = Item.Desc:gsub('<.->', '')
- Item.Desc = Item.Desc:gsub('%s%s+', ' ')
- end
- Item.Date, Item.AllDay, Item.RealDate = IdentifyDate(Item.Date, t)
- table.insert(Items, Item)
- end
- table.sort(Items, compareTime)
- -- IDENTIFY DUPLICATES
- for i, OldItem in ipairs(Feeds[f]) do
- for j, NewItem in ipairs(Items) do
- if NewItem.ID == OldItem.ID then
- Feeds[f][i].Match = j
- Items[j].Unread = OldItem.Unread
- if NewItem.RealDate == 0 then
- Items[j].Date = OldItem.Date
- Items[j].AllDay = OldItem.AllDay
- end
- end
- end
- end
- -- CLEAR DUPLICATES OR ALL HISTORY
- local KeepOldItems = SELF:GetNumberOption('KeepOldItems', 0)
- if (KeepOldItems == 1) and Type.MergeItems then
- for i = #Feeds[f], 1, -1 do
- if Feeds[f][i].Match then
- table.remove(Feeds[f], i)
- end
- end
- else
- for i = 1, #Feeds[f] do
- table.remove(Feeds[f])
- end
- end
- -- ADD NEW ITEMS
- for i = #Items, 1, -1 do
- if Items[i] then
- if t == 'GoogleCalendar' then
- if (Items[i].Date > os.time()) then
- table.insert(Feeds[f], 1, Items[i])
- end
- else
- table.insert(Feeds[f], 1, Items[i])
- end
- end
- end
- -- CHECK NUMBER OF ITEMS
- local MaxItems = SELF:GetNumberOption('MaxItems', nil)
- local MaxItems = (MaxItems > 0) and MaxItems or nil
- if #Feeds[f] == 0 then
- Feeds[f].Error = {
- Description = 'No items found.',
- Title = Feeds[f]['Title'],
- Link = Feeds[f]['Link']
- }
- return false
- elseif MaxItems and (#Feeds[f] > MaxItems) then
- for i = #Feeds[f], (MaxItems + 1), -1 do
- table.remove(Feeds[f])
- end
- end
- -- MODULES
- EventFile_Update(f)
- HistoryFile_Update(f)
- -- CLEAR ERRORS FROM PREVIOUS UPDATE
- Feeds[f].Error = nil
- -- RESET USER INPUT
- UserInput = false
- end
- return true
- end
- -----------------------------------------------------------------------
- -- OUTPUT
- function Output()
- local Queue = {}
- -- MAKE SYNTAX PRETTIER
- local Feed = Feeds[f]
- local Type = Types[Feed.Type]
- local Error = Feed.Error
- -- BUILD QUEUE
- Queue['CurrentFeed'] = f
- Queue['NumberOfItems'] = #Feed
- -- CHECK FOR INPUT ERRORS
- local MinItems = SELF:GetNumberOption('MinItems', 0)
- local Timestamp = SELF:GetOption('Timestamp', '%I.%M %p on %d %B %Y')
- if Error then
- -- ERROR; QUEUE MESSAGES
- Queue['FeedTitle'] = Error.Title
- Queue['FeedLink'] = Error.Link
- Queue['Item1Title'] = Error.Description
- Queue['Item1Link'] = Error.Link
- Queue['Item1Desc'] = ''
- Queue['Item1Date'] = ''
- Queue['Item1Unread'] = 0
- for i = 2, MinItems do
- Queue['Item'..i..'Title'] = ''
- Queue['Item'..i..'Link'] = ''
- Queue['Item'..i..'Desc'] = ''
- Queue['Item'..i..'Date'] = ''
- Queue['Item'..i..'Unread'] = 0
- end
- else
- -- NO ERROR; QUEUE FEED
- Queue['FeedTitle'] = Feed.Title
- Queue['FeedLink'] = Feed.Link or ''
- for i = 1, math.max(#Feed, MinItems) do
- local Item = Feed[i] or {}
- Queue['Item'..i..'Title'] = Item.Title or ''
- Queue['Item'..i..'Link'] = Item.Link or Feed.Link or ''
- Queue['Item'..i..'Desc'] = Item.Desc or ''
- Queue['Item'..i..'Unread'] = Item.Unread or ''
- Queue['Item'..i..'Date'] = Item.Date and os.date(Timestamp, Item.Date) or ''
- end
- end
- -- SET VARIABLES
- local VariablePrefix = SELF:GetOption('VariablePrefix', '')
- for k, v in pairs(Queue) do
- SKIN:Bang('!SetVariable', VariablePrefix..k, v)
- end
- -- FINISH ACTION
- local FinishAction = SELF:GetOption('FinishAction', '')
- if FinishAction ~= '' then
- SKIN:Bang(FinishAction)
- end
- return Error and Error.Description or 'Finished #'..f..' ('..Feed.MeasureName..'). Name: '..Feed.Title..'. Type: '..Feed.Type..'. Items: '..#Feed..'.'
- end
- -----------------------------------------------------------------------
- -- EXTERNAL COMMANDS
- function Refresh(a)
- a = a and tonumber(a) or f
- if a == f then
- SKIN:Bang('!UpdateMeasure', SELF:GetName())
- else
- Input(a)
- end
- end
- function Show(a)
- f = tonumber(a)
- SKIN:Bang('!UpdateMeasure', SELF:GetName())
- end
- function ShowNext()
- f = (f % #Feeds) + 1
- SKIN:Bang('!UpdateMeasure', SELF:GetName())
- end
- function ShowPrevious()
- f = (f == 1) and #Feeds or (f - 1)
- SKIN:Bang('!UpdateMeasure', SELF:GetName())
- end
- function MarkRead(a, b)
- b = b and tonumber(b) or f
- Feeds[b][a].Unread = 0
- UserInput = true
- SKIN:Bang('!UpdateMeasure', SELF:GetName())
- end
- function MarkUnread(a, b)
- b = b and tonumber(b) or f
- Feeds[b][a].Unread = 1
- UserInput = true
- SKIN:Bang('!UpdateMeasure', SELF:GetName())
- end
- function ToggleUnread(a, b)
- b = b and tonumber(b) or f
- Feeds[b][a].Unread = 1 - Feeds[b][a].Unread
- UserInput = true
- SKIN:Bang('!UpdateMeasure', SELF:GetName())
- end
- -----------------------------------------------------------------------
- -- TYPES
- function DefineTypes()
- Types = {
- RSS = {
- MatchLink = '<link.->(.-)</link>',
- MatchItem = '<item.-</item>',
- MatchItemID = '<guid.->(.-)</guid>',
- MatchItemLink = '<link.->(.-)</link>',
- MatchItemDesc = '<description.->(.-)</description>',
- MatchItemDate = '<pubDate.->(.-)</pubDate>',
- MergeItems = true,
- ParseDate = function(s)
- local Date = {}
- local MatchTime = '%a%a%a, (%d%d) (%a%a%a) (%d%d%d%d) (%d%d)%:(%d%d)%:(%d%d) (.-)$'
- local MatchDate = '%a%a%a, (%d%d) (%a%a%a) (%d%d%d%d)$'
- if s:match(MatchTime) then
- Date.day, Date.month, Date.year, Date.hour, Date.min, Date.sec, Date.Offset = s:match(MatchTime)
- elseif s:match(MatchDate) then
- Date.day, Date.month, Date.year = s:match(MatchDate)
- end
- return (Date.year and Date.month and Date.day) and Date or nil
- end
- },
- Atom = {
- MatchLink = '<link.-href=["\'](.-)["\']',
- MatchItem = '<entry.-</entry>',
- MatchItemID = '<id.->(.-)</id>',
- MatchItemLink = '<link.-href=["\'](.-)["\']',
- MatchItemDesc = '<summary.->(.-)</summary>',
- MatchItemDate = '<modified.->(.-)</modified>',
- MergeItems = true,
- ParseDate = function(s)
- local Date = {}
- local MatchTime = '(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d)%:(%d%d)%:(%d%d)(.-)$'
- local MatchDate = '(%d%d%d%d)%-(%d%d)%-(%d%d)$'
- if s:match(MatchTime) then
- Date.year, Date.month, Date.day, Date.hour, Date.min, Date.sec, Date.Offset = s:match(MatchTime)
- elseif s:match(MatchDate) then
- Date.year, Date.month, Date.day = s:match(MatchDate)
- end
- return Date
- end
- },
- GoogleCalendar = {
- MatchLink = 'UID:(%S+)',
- MatchItem = 'BEGIN:VEVENT.-END:VEVENT',
- MatchItemID = 'UID:(%S+)',
- MatchItemLink = 'UID:(%S+)',
- MatchItemDesc = 'DESCRIPTION:(%S+)',
- --MatchItemDate = 'DTSTART.-DTSTAMP',
- MatchItemDate = 'DTSTART.-UID',
- MergeItems = false,
- ParseDate = function(s)
- local Date = {}
- -- For finding the Offset
- local DS = {}
- local StampMatch = 'DTSTAMP:(%d%d%d%d)(%d%d)(%d%d)T(%d%d)(%d%d)(%d%d)(%S+)'
- if s:match(StampMatch) then
- DS.year, DS.month, DS.day, DS.hour, DS.min, DS.sec, DS.Offset = s:match(StampMatch)
- -- Remove date from main string so it doesn't interfere with date matching below
- s = s:gsub('DTSTAMP:' .. DS.year .. DS.month .. DS.day .. 'T' .. DS.hour .. DS.min .. DS.sec .. DS.Offset, '')
- end
- local MatchUntil = 'UNTIL=(%d%d%d%d)(%d%d)(%d%d)'
- if s:match(MatchUntil) then
- Date.year, Date.month, Date.day = s:match('(%d%d%d%d)(%d%d)(%d%d)')
- return Date
- -- Monthly recurring events
- elseif s:match('BYMONTHDAY=(%S+)') then
- Date.year = os.date('%Y')
- Date.month = os.date('%m')
- Date.day = s:match('BYMONTHDAY=(%S+)')
- -- If date is in the past then add a month
- if os.time(Date) < os.time() then
- if tonumber(Date.month) < 12 then
- Date.month = Date.month + 1
- else
- Date.month = 1
- Date.year = Date.year + 1
- end
- end
- return Date
- -- Yearly recurring events
- elseif s:match('FREQ=YEARLY') then
- -- Match and set to this year
- Date.year, Date.month, Date.day = s:match('DTSTART;VALUE=DATE:(%d%d%d%d)(%d%d)(%d%d)')
- Date.year = os.date('%Y')
- return Date
- -- Day is not numeric, e.g. '1FR' for first Friday of month or '2TH' for 2nd Thursday
- elseif s:match('BYDAY=(%d+)(%u+)') then
- local days1 = {['Mon']=0, ['Tue']=1, ['Wed']=2, ['Thu']=3, ['Fri']=4, ['Sat']=5, ['Sun']=6}
- local days2 = {['MO']=0, ['TU']=1, ['WE']=2, ['TH']=3, ['FR']=4, ['SA']=5, ['SU']=6}
- -- Get first day of current month
- local d = os.date('%a', os.time{ year=os.date('%Y'), month=os.date('%m'), day=1 })
- local wday = {}
- wday.n, wday.d = s:match('BYDAY=(%d+)(%u+)')
- local daydiff = (days2[wday.d] - days1[d]) + 1
- if daydiff < 0 then
- daydiff = daydiff + 7
- end
- -- nth of day
- daydiff = daydiff + ((wday.n - 1) * 7)
- Date.year = os.date('%Y')
- Date.month = os.date('%m')
- Date.day = daydiff
- -- If date is in the past then add a month
- if os.time(Date) < os.time() then
- if tonumber(Date.month) < 12 then
- Date.month = Date.month + 1
- else
- Date.month = 1
- Date.year = Date.year + 1
- end
- end
- -- Match time from original record
- local Date2 = {}
- if s:match('(%d%d%d%d)(%d%d)(%d%d)T(%d%d)(%d%d)(%d%d)') then
- Date2.year, Date2.month, Date2.day, Date2.hour, Date2.min, Date2.sec = s:match('(%d%d%d%d)(%d%d)(%d%d)T(%d%d)(%d%d)(%d%d)')
- Date.hour = Date2.hour
- Date.min = Date2.min
- Date.sec = Date2.sec
- Date.Offset = DS.Offset
- end
- return Date
- end
- -- Standard date fallback
- local MatchTime = '(%d%d%d%d)(%d%d)(%d%d)T(%d%d)(%d%d)(%d%d)'
- local MatchDate = '(%d%d%d%d)(%d%d)(%d%d)'
- if s:match(MatchTime) then
- Date.year, Date.month, Date.day, Date.hour, Date.min, Date.sec = s:match(MatchTime)
- Date.Offset = DS.Offset
- else
- Date.year, Date.month, Date.day = s:match(MatchDate)
- end
- return Date
- end
- },
- RememberTheMilk = {
- MatchLink = '<link.-rel=.-alternate.-href=["\'](.-)["\']',
- MatchItem = '<entry.-</entry>',
- MatchItemID = '<id.->(.-)</id>',
- MatchItemLink = '<link.-href=["\'](.-)["\']',
- MatchItemDesc = '<summary.->(.-)</summary>',
- MatchItemDate = '<span class=["\']rtm_due_value["\']>(.-)</span>',
- MergeItems = false,
- ParseDate = function(s)
- local Date = {}
- local MatchTime = '%a%a%a (%d+) (%a%a%a) (%d+) at (%d+)%:(%d+)(%a%a)' -- e.g. 'Wed 7 Nov 12 at 3:17PM'
- local MatchDate = '%a%a%a (%d+) (%a%a%a) (%d+)' -- e.g. 'Tue 25 Dec 12'
- if s:match(MatchTime) then
- Date.day, Date.month, Date.year, Date.hour, Date.min, Date.Meridiem = s:match(MatchTime)
- elseif s:match(MatchDate) then
- Date.day, Date.month, Date.year = s:match(MatchDate)
- end
- return Date
- end
- }
- }
- end
- -------------------------
- function IdentifyType(s)
- -- COLLAPSE CONTAINER TAGS
- for _, v in ipairs{ 'item', 'entry' } do
- s = s:gsub('<'..v..'.->.+</'..v..'>', '<'..v..'></'..v..'>') -- e.g. '<entry.->.+</entry>' --> '<entry></entry>'
- end
- --DEFINE RSS MARKER TESTS
- --Each of these test functions will be run in turn, until one of them gets a solid match on the format type.
- local TestRSS = {
- function(a)
- -- If the feed contains these tags outside of <item> or <entry>, RSS is confirmed.
- for _, v in ipairs{ '<rss', '<channel', '<lastBuildDate', '<pubDate', '<ttl', '<description' } do
- if a:match(v) then
- return 'RSS'
- end
- end
- return false
- end,
- function(a)
- -- Alternatively, if the feed contains these tags outside of <item> or <entry>, Atom is confirmed.
- for _, v in ipairs{ '<feed', '<subtitle' } do
- if a:match(v) then
- return 'Atom'
- end
- end
- return false
- end,
- function(a)
- -- Alternatively, if the feed contains these tags ICAL is confirmed.
- for _, v in ipairs{ 'DTSTART', 'DTEND' } do
- if a:match(v) then
- return 'Ical'
- end
- end
- return false
- end,
- function(a)
- -- If no markers are present, we search for <item> or <entry> tags to confirm the type.
- local HaveItems = a:match('<item')
- local HaveEntries = a:match('<entry')
- local HaveIcal = a:match('DTSTART')
- if HaveItems and not HaveEntries then
- return 'RSS'
- elseif HaveEntries and not HaveItems then
- return 'Atom'
- elseif HaveIcal then
- return 'Ical'
- else
- -- If both kinds of tags are present, and no markers are given, then I give up
- -- because your feed is ridiculous. And if neither tag is present, then no type
- -- can be confirmed (and there would be no usable data anyway).
- return false
- end
- end
- }
- -- RUN RSS MARKER TESTS
- local Class = false
- for _, v in ipairs(TestRSS) do
- Class = v(s)
- if Class then break end
- end
- -- DETECT SUBTYPE AND RETURN
- if Class == 'RSS' then
- return 'RSS'
- elseif Class == 'Atom' then
- if s:match('xmlns:gCal') then
- return 'GoogleCalendar'
- elseif s:match('<subtitle>rememberthemilk.com</subtitle>') then
- return 'RememberTheMilk'
- else
- return 'Atom'
- end
- elseif Class == 'Ical' then
- return 'GoogleCalendar'
- else
- return false
- end
- end
- -------------------------
- function IdentifyDate(s, t)
- local Date = nil
- Date = s and Types[t].ParseDate(s) or {}
- Date.year = tonumber(Date.year) or nil
- Date.month = tonumber(Date.month) or MonthAcronyms[Date.month] or nil
- Date.day = tonumber(Date.day) or nil
- Date.hour = tonumber(Date.hour) or nil
- Date.min = tonumber(Date.min) or nil
- Date.sec = tonumber(Date.sec) or 0
- -- FIND ENOUGH ELEMENTS, OR DEFAULT TO RETRIEVAL DATE
- local RealDate, AllDay
- if (Date.year and Date.month and Date.day) then
- RealDate = 1
- -- DETECT ALL-DAY EVENT
- if (Date.hour and Date.min) then
- AllDay = 0
- else
- AllDay = 1
- Date.hour = 0
- Date.min = 0
- end
- -- GET CURRENT LOCAL TIME, UTC OFFSET
- -- These values are referenced in several procedures below.
- local UTC = os.date('!*t')
- local LocalTime = os.date('*t')
- local DaylightSavings = LocalTime.isdst and 3600 or 0
- local LocalOffset = os.time(LocalTime) - os.time(UTC) + DaylightSavings
- -- CHANGE 12-HOUR to 24-HOUR
- if Date.Meridiem then
- if (Date.Meridiem == 'AM') and (Date.hour == 12) then
- Date.hour = 0
- elseif (Date.Meridiem == 'PM') and (Date.hour < 12) then
- Date.hour = Date.hour + 12
- end
- end
- -- FIND CLOSEST MATCH FOR TWO-DIGIT YEAR
- if Date.year < 100 then
- local CurrentYear = LocalTime.year
- local CurrentCentury = math.floor(CurrentYear / 100) * 100
- local IfThisCentury = CurrentCentury + Date.year
- local IfNextCentury = CurrentCentury + Date.year + 100
- if math.abs(CurrentYear - IfThisCentury) < math.abs(CurrentYear - IfNextCentury) then
- Date.year = IfThisCentury
- else
- Date.year = IfNextCentury
- end
- end
- -- GET INPUT OFFSET FROM UTC (OR DEFAULT TO LOCAL)
- if (Date.Offset) and (Date.Offset ~= '') then
- if Date.Offset:match('%a') then
- Date.Offset = TimeZones[Date.Offset] and (TimeZones[Date.Offset] * 3600) or 0
- elseif Date.Offset:match('%d') then
- local Direction, Hours, Minutes = Date.Offset:match('^([^%d]-)(%d+)[^%d]-(%d%d)')
- Direction = Direction:match('%-') and -1 or 1
- Hours = tonumber(Hours) * 3600
- Minutes = tonumber(Minutes) and (tonumber(Minutes) * 60) or 0
- Date.Offset = (Hours + Minutes) * Direction
- end
- else
- Date.Offset = LocalOffset
- end
- -- RETURN CONVERTED DATE
- Date = os.time(Date) + LocalOffset - Date.Offset
- else
- -- NO USABLE DATE FOUND; USE RETRIEVAL DATE INSTEAD
- RealDate = 0
- AllDay = 0
- Date = os.time()
- end
- return Date, AllDay, RealDate
- end
- -----------------------------------------------------------------------
- -- EVENT FILE MODULE
- function EventFile_Initialize()
- local EventFiles = {}
- local AllEventFiles = SELF:GetOption('EventFile', '')
- for EventFile in AllEventFiles:gmatch('[^%|]+') do
- table.insert(EventFiles, EventFile)
- end
- for i, v in ipairs(Feeds) do
- local EventFile = EventFiles[i] or SELF:GetName()..'_Feed'..i..'Events.xml'
- Feeds[i].EventFile = SKIN:MakePathAbsolute(EventFile)
- end
- end
- function EventFile_Update(a)
- local f = a or f
- local WriteEvents = SELF:GetNumberOption('WriteEvents', 0)
- if (WriteEvents == 1) and (Feeds[f].Type == 'GoogleCalendar') then
- -- CREATE XML TABLE
- local WriteLines = {}
- table.insert(WriteLines, '<EventFile Title="'..Feeds[f].Title..'">')
- for i, v in ipairs(Feeds[f]) do
- local ItemDate = os.date('*t', v.Date)
- table.insert(WriteLines, '<Event Month="'..ItemDate['month']..'" Day="'..ItemDate['day']..'" Desc="'..v.Title..'"/>')
- end
- table.insert(WriteLines, '</EventFile>')
- -- WRITE FILE
- local WriteFile = io.output(Feeds[f].EventFile, 'w')
- if WriteFile then
- local WriteContent = table.concat(WriteLines, '\r\n')
- WriteFile:write(WriteContent)
- WriteFile:close()
- else
- SKIN:Bang('!Log', SELF:GetName()..': cannot open file: '..Feeds[f].EventFile)
- end
- end
- end
- -----------------------------------------------------------------------
- -- HISTORY FILE MODULE
- function HistoryFile_Initialize()
- -- DETERMINE FILEPATH
- HistoryFile = SELF:GetOption('HistoryFile', SELF:GetName()..'History.xml')
- HistoryFile = SKIN:MakePathAbsolute(HistoryFile)
- -- CREATE HISTORY DATABASE
- History = {}
- -- CHECK IF FILE EXISTS
- local ReadFile = io.open(HistoryFile)
- if ReadFile then
- local ReadContent = ReadFile:read('*all')
- ReadFile:close()
- -- PARSE HISTORY FROM LAST SESSION
- for ReadFeedURL, ReadFeed in ReadContent:gmatch('<feed URL=(%b"")>(.-)</feed>') do
- local ReadFeedURL = ReadFeedURL:match('^"(.-)"$')
- History[ReadFeedURL] = {}
- for ReadItem in ReadFeed:gmatch('<item>(.-)</item>') do
- local Item = {}
- for Key, Value in ReadItem:gmatch('<(.-)>(.-)</.->') do
- Value = Value:gsub('<', '<')
- Value = Value:gsub('>', '>')
- Item[Key] = Value
- end
- Item.Date = tonumber(Item.Date) or Item.Date
- Item.Unread = tonumber(Item.Unread)
- table.insert(History[ReadFeedURL], Item)
- end
- end
- end
- -- ADD HISTORY TO MAIN DATABASE
- -- For each feed, if URLs match, add all contents from History[h] to Feeds[f].
- for f, Feed in ipairs(Feeds) do
- local h = Feed.Measure:GetOption('URL')
- Feeds[f].URL = h
- if History[h] then
- for _, Item in ipairs(History[h]) do
- table.insert(Feeds[f], Item)
- end
- end
- end
- end
- function HistoryFile_Update(a)
- local f = a or f
- -- CLEAR AND REBUILD HISTORY
- local h = Feeds[f].URL
- History[h] = {}
- for i, Item in ipairs(Feeds[f]) do
- table.insert(History[h], Item)
- end
- -- WRITE HISTORY IF REQUESTED
- WriteHistory()
- end
- function WriteHistory()
- local WriteHistory = SELF:GetNumberOption('WriteHistory', 0)
- if WriteHistory == 1 then
- -- GENERATE XML TABLE
- local WriteLines = {}
- for WriteURL, WriteFeed in pairs(History) do
- table.insert(WriteLines, string.format( '<feed URL=%q>', WriteURL))
- for _, WriteItem in ipairs(WriteFeed) do
- table.insert(WriteLines, '\t<item>')
- for Key, Value in pairs(WriteItem) do
- Value = string.gsub(Value, '<', '<')
- Value = string.gsub(Value, '>', '>')
- table.insert(WriteLines, string.format( '\t\t<%s>%s</%s>', Key, Value, Key))
- end
- table.insert(WriteLines, '\t</item>')
- end
- table.insert(WriteLines, '</feed>')
- end
- -- WRITE XML TO FILE
- local WriteFile = io.open(HistoryFile, 'w')
- if WriteFile then
- local WriteContent = table.concat(WriteLines, '\n')
- WriteFile:write(WriteContent)
- WriteFile:close()
- else
- SKIN:Bang('!Log', SELF:GetName()..': cannot open file: '..HistoryFile)
- end
- end
- end
- function ClearHistory()
- local DeleteFile = io.open(HistoryFile)
- if DeleteFile then
- DeleteFile:close()
- os.remove(HistoryFile)
- SKIN:Bang('!Log', SELF:GetName()..': deleted history cache at '..HistoryFile)
- end
- SKIN:Bang('!Refresh')
- end
- -----------------------------------------------------------------------
- -- CONSTANTS
- TimeZones = {
- IDLW = -12, -- International Date Line West
- NT = -11, -- Nome
- CAT = -10, -- Central Alaska
- HST = -10, -- Hawaii Standard
- HDT = -9, -- Hawaii Daylight
- YST = -9, -- Yukon Standard
- YDT = -8, -- Yukon Daylight
- PST = -8, -- Pacific Standard
- PDT = -7, -- Pacific Daylight
- MST = -7, -- Mountain Standard
- MDT = -6, -- Mountain Daylight
- CST = -6, -- Central Standard
- CDT = -5, -- Central Daylight
- EST = -5, -- Eastern Standard
- EDT = -4, -- Eastern Daylight
- AST = -3, -- Atlantic Standard
- ADT = -2, -- Atlantic Daylight
- WAT = -1, -- West Africa
- GMT = 0, -- Greenwich Mean
- UTC = 0, -- Universal (Coordinated)
- Z = 0, -- Zulu, alias for UTC
- WET = 0, -- Western European
- BST = 1, -- British Summer
- CET = 1, -- Central European
- MET = 1, -- Middle European
- MEWT = 1, -- Middle European Winter
- MEST = 2, -- Middle European Summer
- CEST = 2, -- Central European Summer
- MESZ = 2, -- Middle European Summer
- FWT = 1, -- French Winter
- FST = 2, -- French Summer
- EET = 2, -- Eastern Europe, USSR Zone 1
- EEST = 3, -- Eastern European Daylight
- WAST = 7, -- West Australian Standard
- WADT = 8, -- West Australian Daylight
- CCT = 8, -- China Coast, USSR Zone 7
- JST = 9, -- Japan Standard, USSR Zone 8
- EAST = 10, -- Eastern Australian Standard
- EADT = 11, -- Eastern Australian Daylight
- GST = 10, -- Guam Standard, USSR Zone 9
- NZT = 12, -- New Zealand
- NZST = 12, -- New Zealand Standard
- NZDT = 13, -- New Zealand Daylight
- IDLE = 12 -- International Date Line East
- }
- MonthAcronyms = {
- Jan = 1,
- Feb = 2,
- Mar = 3,
- Apr = 4,
- May = 5,
- Jun = 6,
- Jul = 7,
- Aug = 8,
- Sep = 9,
- Oct = 10,
- Nov = 11,
- Dec = 12
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement