mcbot/cmds/tvcal.lua
author Mikael Berthe <mikael@lilotux.net>
Mon, 26 Mar 2012 08:37:26 +0200
changeset 59 0a39d217b80a
parent 58 4c3028c21ccb
child 61 5d807892b439
permissions -rw-r--r--
Fix tvcal regex Some shows were skipped (first, last, pilots...)!

#! /usr/bin/env lua

-- Data extractor for http://www.pogdesign.co.uk/cat/
-- Mikael BERTHE, 2010-04-18

require "libs.shcmd"

local tvcal = { ["desc"] = "Display TV calendar" }

local tvcaldata = {}
local tvcaldata_timestamp

-- Function from PIL
local function pairsByKeys (t, f)
    local a = {}
    for n in pairs(t) do table.insert(a, n) end
    table.sort(a, f)
    local i = 0      -- iterator variable
    local iter = function ()   -- iterator function
        i = i + 1
        if a[i] == nil then return nil
        else return a[i], t[a[i]]
        end
    end
    return iter
end

local function parse_webpage (url)
    local contents = shcmd("curl "..url)

    if not contents then
        return nil, "Could not fetch calendar, please try again later!"
    end

    -- Regex for each day
    local tabregex = '<td id="d_(%d+_%d+_%d%d%d%d)" class="t?o?day"%s*>'..
                     '%s*(.-)%s*</td>'

    -- Regex for parsing one day
    local epregex = '<p[^>]*>'..
                      '<a href=[^>]+>([^<]+)</a>'..       -- Name
                      '<br /><a href=[^>]+>([^<]+)</a>'.. -- Episode
                    '</p>'

    -- loop over all days
    for day, daytab in string.gmatch(contents, tabregex) do
        local d, m, y = day:match("^(%d+)_(%d+)_(%d%d%d%d)$")
        day = string.format("%04d-%02d-%02d", y, m, d)

        local shows = {}

        -- Loop over all episodes of a day
        for name, ep in string.gmatch(daytab, epregex) do
            name = name:gsub("&quot;", "\""):gsub("&amp;", "&")
            name = name:gsub("&lt;", "<"):gsub("&gt;", ">")
            name = name:gsub("&#39;", "'")

            local obj = {
                ["name"] = name,
                ["ep"] = ep
                }
            table.insert(shows, obj)
        end
        tvcaldata[day] = shows
    end

    tvcaldata_timestamp = os.date("%F")
    return true
end

local function tvcal_by_date (d)
    if not tvcaldata[d] then
        return "No episode found for this date ("..d..")"
    end

    local r = "Shows on " .. d .. ":\n"
    for i,j in ipairs(tvcaldata[d]) do
        r = r .. j.name .. " (" ..j.ep .. ")\n"
    end
    r = r:gsub("\n+$", "")
    return r
end

local function tvcal_by_name (name, plain)
    local action = plain and "contain" or "match"
    local r = ""
    name = name:lower()

    for day, obj in pairsByKeys(tvcaldata) do
        for i,j in ipairs(obj) do
            if j.name:lower():find(name, 1, plain) then
                r = r .. day .. "\t" .. j.name .. " (" ..j.ep .. ")\n"
            end
        end
    end

    if r == "" then
        return "No episode found "..action.."ing this string"
    end
    r = r:gsub("\n+$", "")
    return "Episodes "..action.."ing \""..name.."\":\n" .. r
end

function tvcal.cmd (arg)
    if not tvcaldata_timestamp or tvcaldata_timestamp ~= os.date("%F") then
        tvcaldata = {} -- Empty the previous list
        local r, err = parse_webpage("http://www.pogdesign.co.uk/cat/")
        if not r then return nil, err end
        -- Fetch next month as well...
        local m = tonumber(os.date("%m"))+1
        local y = os.date("%Y")
        if m > 12 then m = 1; y = y + 1; end
        parse_webpage("http://www.pogdesign.co.uk/cat/"..m.."-"..y)
    end

    if not arg or arg == "today" then
        arg = os.date("%F")
    elseif arg == "tomorrow" then
        arg = os.date("%F", os.date("%s")+86400)
    elseif arg:match("^%+%d+$") then
        local n = tonumber(arg:match("^%+(%d+)$"))
        if n < 60 then
            arg = os.date("%F", os.date("%s")+86400*n)
        end
    end

    if arg:match("^%d%d%d%d%-%d%d%-%d%d$") then
        return tvcal_by_date(arg)
    end

    -- Is it a pattern? (string surrounded by quotes)
    local plain = arg:match('^"(.*)"$')
    if plain then
        arg = plain
        plain = false
    else
        plain = true
    end
    return tvcal_by_name(arg, plain)
end

mcbot_register_command("tvcal", tvcal)