Skip to content

Instantly share code, notes, and snippets.

@hu553in
Last active March 11, 2025 20:13
Show Gist options
  • Save hu553in/1ac9f48c8368398bcc1018748537e54e to your computer and use it in GitHub Desktop.
Save hu553in/1ac9f48c8368398bcc1018748537e54e to your computer and use it in GitHub Desktop.
Yandex Cloud function to get RSS feed built from Todoist tasks with "rss" label
package main
import (
"context"
"encoding/json"
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"os"
"slices"
"time"
)
type Response struct {
StatusCode int `json:"statusCode"`
Body interface{} `json:"body"`
}
type TodoistTask struct {
ID string `json:"id"`
Content string `json:"content"`
Description string `json:"description"`
CreatedAt string `json:"created_at"`
URL string `json:"url"`
Labels []string `json:"labels"`
}
type RSS struct {
XMLName xml.Name `xml:"rss"`
Version string `xml:"version,attr"`
Channel Channel `xml:"channel"`
}
type Channel struct {
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
Items []Item `xml:"item"`
}
type Item struct {
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
PubDate string `xml:"pubDate"`
GUID string `xml:"guid"`
}
func Handler(ctx context.Context) (*Response, error) {
token := os.Getenv("TODOIST_TOKEN")
if token == "" {
return nil, fmt.Errorf("TODOIST_TOKEN environment variable is not set")
}
client := &http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.todoist.com/rest/v2/tasks", nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
bodyBytes, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("failed to fetch tasks: %s", string(bodyBytes))
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var tasks []TodoistTask
if err := json.Unmarshal(bodyBytes, &tasks); err != nil {
return nil, err
}
var filtered []TodoistTask
for _, task := range tasks {
if slices.Contains(task.Labels, "rss") {
filtered = append(filtered, task)
}
}
rss := RSS{
Version: "2.0",
Channel: Channel{
Title: "Todoist Tasks",
Link: "https://todoist.com",
Description: "RSS feed of Todoist tasks",
},
}
for _, task := range filtered {
t, err := time.Parse(time.RFC3339Nano, task.CreatedAt)
if err != nil {
t = time.Now()
}
item := Item{
Title: task.Content,
Link: task.URL,
Description: task.Description,
PubDate: t.Format(time.RFC1123Z),
GUID: task.ID,
}
rss.Channel.Items = append(rss.Channel.Items, item)
}
output, err := xml.MarshalIndent(rss, "", " ")
if err != nil {
return nil, err
}
rssFeed := xml.Header + string(output)
return &Response{
StatusCode: 200,
Body: rssFeed,
}, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment