mcbot/cmds/tvcal.lua
author Mikael Berthe <mikael@lilotux.net>
Tue, 27 Nov 2012 16:26:04 +0100
changeset 66 d9c00a9fe9d5
parent 63 31f967ba9e1f
permissions -rw-r--r--
Add notices before public release

#! /usr/bin/env lua

--  This module is part of the McBot / mcabbot project
--  Data extractor for http://www.pogdesign.co.uk/cat/
--
-- Copyright (C) 2010-2012 Mikael Berthe <mikael@lilotux.net>
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or (at
-- your option) any later version.
--
-- Please check the license in the COPYING file at the root of the tree.
--

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

    -- Work around frackin ugly end of line characters... :/
    local eol = "[%s"..string.char(13).."]*"

    -- 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>'..eol..        -- Name
                      '<br /><a href=[^>]+>([^<]+)</a>'..eol..  -- 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;", "'")
            name = name:gsub("%s*\n%s*", "  ")

            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)