diff --git a/_site/content/posts/modules/trello.md b/_site/content/posts/modules/trello.md
new file mode 100644
index 00000000..c8649b69
--- /dev/null
+++ b/_site/content/posts/modules/trello.md
@@ -0,0 +1,87 @@
+---
+title: "Trello"
+date: 2018-05-10T10:44:35-07:00
+draft: false
+---
+
+Displays all Trello cards on specified lists.
+
+
+
+## Source Code
+
+```bash
+wtf/trello/
+```
+
+## Required ENV Variables
+
+Key: `WTF_TRELLO_APP_KEY`
+Value: Your Trello App Key.
+Key: `WTF_TRELLO_ACCESS_TOKEN`
+Value: Your Trello Access Token.
+
+_You can get your API key at: trello.com/app-key._
+
+## Keyboard Commands
+
+None.
+
+## Configuration
+
+### Single Trello List
+
+```yaml
+trello:
+ board: Main
+ enabled: true
+ list: "Todo"
+ position:
+ height: 1
+ left: 2
+ top: 0
+ width: 1
+ refreshInterval: 3600
+ username: myname
+```
+
+### Multiple Trello Lists
+
+If you want to monitor multiple Trello lists, use the following
+configuration (note the difference in `list`):
+
+```yaml
+trello:
+ board: Main
+ enabled: true
+ list: ["Todo", "Done"]
+ position:
+ height: 1
+ left: 2
+ top: 0
+ width: 1
+ refreshInterval: 3600
+ username: myname
+```
+
+### Attributes
+
+`board`
+The name of the Trello board.
+
+`enabled`
+Determines whether or not this module is executed and if its data displayed onscreen.
+Values: `true`, `false`.
+
+`list`
+The Trello lists to fetch cards from.
+
+`refreshInterval`
+How often, in seconds, this module will update its data.
+Values: A positive integer, `0..n`.
+
+`username`
+Your Trello username.
+
+`position`
+Where in the grid this module's widget will be displayed.
diff --git a/_site/static/imgs/modules/trello.png b/_site/static/imgs/modules/trello.png
new file mode 100644
index 00000000..79fa6965
Binary files /dev/null and b/_site/static/imgs/modules/trello.png differ
diff --git a/trello/card.go b/trello/card.go
new file mode 100644
index 00000000..e1487c94
--- /dev/null
+++ b/trello/card.go
@@ -0,0 +1,8 @@
+package trello
+
+type TrelloCard struct {
+ ID string
+ Name string
+ List string
+ Description string
+}
diff --git a/trello/client.go b/trello/client.go
new file mode 100644
index 00000000..dcccaf55
--- /dev/null
+++ b/trello/client.go
@@ -0,0 +1,98 @@
+package trello
+
+import (
+ "fmt"
+
+ "github.com/adlio/trello"
+ "github.com/senorprogrammer/wtf/wtf"
+)
+
+func GetCards(client *trello.Client, lists map[string]string) (*SearchResult, error) {
+ boardID, err := getBoardID(client)
+ if err != nil {
+ return nil, err
+ }
+
+ lists, err = getListIDs(client, boardID, lists)
+ if err != nil {
+ return nil, err
+ }
+
+ searchResult := &SearchResult{Total: 0}
+ searchResult.TrelloCards = make(map[string][]TrelloCard)
+
+ for listName, listID := range lists {
+ cards, err := getCardsOnList(client, listID)
+ if err != nil {
+ return nil, err
+ }
+ searchResult.Total = searchResult.Total + len(cards)
+ cardArray := make([]TrelloCard, 0)
+ for _, card := range cards {
+ trelloCard := TrelloCard{
+ ID: card.ID,
+ List: listName,
+ Name: card.Name,
+ Description: card.Desc,
+ }
+ cardArray = append(cardArray, trelloCard)
+ }
+ searchResult.TrelloCards[listName] = cardArray
+ }
+
+ return searchResult, nil
+}
+
+func getBoardID(client *trello.Client) (string, error) {
+ member, err := client.GetMember(wtf.Config.UString("wtf.mods.trello.username"), trello.Defaults())
+ if err != nil {
+ return "", err
+ }
+
+ boards, err := member.GetBoards(trello.Defaults())
+ if err != nil {
+ return "", err
+ }
+
+ for _, board := range boards {
+ if board.Name == wtf.Config.UString("wtf.mods.trello.board") {
+ return board.ID, nil
+ }
+ }
+
+ return "", fmt.Errorf("could not find board with name %s", wtf.Config.UString("wtf.mods.trello.board"))
+}
+
+func getListIDs(client *trello.Client, boardID string, lists map[string]string) (map[string]string, error) {
+ board, err := client.GetBoard(boardID, trello.Defaults())
+ if err != nil {
+ return nil, err
+ }
+
+ boardLists, err := board.GetLists(trello.Defaults())
+ if err != nil {
+ return nil, err
+ }
+
+ for _, list := range boardLists {
+ if _, ok := lists[list.Name]; ok {
+ lists[list.Name] = list.ID
+ }
+ }
+
+ return lists, nil
+}
+
+func getCardsOnList(client *trello.Client, listID string) ([]*trello.Card, error) {
+ list, err := client.GetList(listID, trello.Defaults())
+ if err != nil {
+ return nil, err
+ }
+
+ cards, err := list.GetCards(trello.Defaults())
+ if err != nil {
+ return nil, err
+ }
+
+ return cards, nil
+}
diff --git a/trello/search_result.go b/trello/search_result.go
new file mode 100644
index 00000000..44ca8dc7
--- /dev/null
+++ b/trello/search_result.go
@@ -0,0 +1,6 @@
+package trello
+
+type SearchResult struct {
+ Total int
+ TrelloCards map[string][]TrelloCard
+}
diff --git a/trello/widget.go b/trello/widget.go
new file mode 100644
index 00000000..99c1b64a
--- /dev/null
+++ b/trello/widget.go
@@ -0,0 +1,81 @@
+package trello
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/adlio/trello"
+ "github.com/senorprogrammer/wtf/wtf"
+)
+
+type Widget struct {
+ wtf.TextWidget
+}
+
+func NewWidget() *Widget {
+ widget := Widget{
+ TextWidget: wtf.NewTextWidget(" Trello ", "trello", false),
+ }
+
+ return &widget
+}
+
+/* -------------------- Exported Functions -------------------- */
+
+func (widget *Widget) Refresh() {
+ client := trello.NewClient(os.Getenv("WTF_TRELLO_APP_KEY"), os.Getenv("WTF_TRELLO_ACCESS_TOKEN"))
+
+ // Get the cards
+ searchResult, err := GetCards(client, getLists())
+ widget.UpdateRefreshedAt()
+
+ if err != nil {
+ widget.View.SetWrap(true)
+ widget.View.SetTitle(fmt.Sprintf("%s", widget.Name))
+ fmt.Fprintf(widget.View, "%v", err)
+ } else {
+ widget.View.SetWrap(false)
+ widget.View.SetTitle(
+ fmt.Sprintf(
+ "[white]%s: [green]%s ",
+ widget.Name,
+ wtf.Config.UString("wtf.mods.trello.board"),
+ ),
+ )
+ widget.View.SetText(fmt.Sprintf("%s", widget.contentFrom(searchResult)))
+ }
+}
+
+/* -------------------- Unexported Functions -------------------- */
+func (widget *Widget) contentFrom(searchResult *SearchResult) string {
+ str := ""
+
+ for list, cardArray := range searchResult.TrelloCards {
+ str = fmt.Sprintf("%s [red]Cards in %s[white]\n", str, list)
+ for _, card := range cardArray {
+ str = fmt.Sprintf("%s [green]%s[white]\n", str, card.Name)
+ }
+ str = fmt.Sprintf("%s\n", str)
+ }
+
+ return str
+}
+
+func getLists() map[string]string {
+ list := make(map[string]string)
+ // see if project is set to a single string
+ configPath := "wtf.mods.trello.list"
+ singleList, err := wtf.Config.String(configPath)
+ if err == nil {
+ list[singleList] = ""
+ return list
+ }
+ // else, assume list
+ multiList := wtf.Config.UList(configPath)
+ for _, proj := range multiList {
+ if str, ok := proj.(string); ok {
+ list[str] = ""
+ }
+ }
+ return list
+}
diff --git a/wtf.go b/wtf.go
index 3c89731e..92450c0d 100644
--- a/wtf.go
+++ b/wtf.go
@@ -38,6 +38,7 @@ import (
"github.com/senorprogrammer/wtf/system"
"github.com/senorprogrammer/wtf/textfile"
"github.com/senorprogrammer/wtf/todo"
+ "github.com/senorprogrammer/wtf/trello"
"github.com/senorprogrammer/wtf/weatherservices/prettyweather"
"github.com/senorprogrammer/wtf/weatherservices/weather"
"github.com/senorprogrammer/wtf/wtf"
@@ -213,6 +214,8 @@ func addWidget(app *tview.Application, pages *tview.Pages, widgetName string) {
Widgets = append(Widgets, textfile.NewWidget(app, pages))
case "todo":
Widgets = append(Widgets, todo.NewWidget(app, pages))
+ case "trello":
+ Widgets = append(Widgets, trello.NewWidget())
case "weather":
Widgets = append(Widgets, weather.NewWidget(app, pages))
default: