mirror of https://github.com/matrix-org/go-neb.git
Browse Source
Add mtgcard service
Add mtgcard service
This service makes API calls to https://scryfall.com/docs/api/ and returns information about "Magic: the Gathering" cards. It provides a command: !mtcard card name And it provides expansion for: [[card name]] Both will return an html message with card information, links, and price. I'm curious if you folks see it relevant to be added to go-neb or not. If you want to see it live, hop to #magicthegathering:matrix.org (currently running non-dedicated).pull/267/head
Víctor Cuadrado Juan
6 years ago
No known key found for this signature in database
GPG Key ID: 223F15CA5AAF0E78
3 changed files with 170 additions and 0 deletions
-
1README.md
-
1src/github.com/matrix-org/go-neb/goneb.go
-
168src/github.com/matrix-org/go-neb/services/mtgcard/mtgcard.go
@ -0,0 +1,168 @@ |
|||
// Package mtgcard implements a Service which adds !commands and mentions for scryfall api.
|
|||
package mtgcard |
|||
|
|||
import ( |
|||
"bytes" |
|||
"encoding/json" |
|||
"fmt" |
|||
"html" |
|||
"net/http" |
|||
"net/url" |
|||
"regexp" |
|||
"strings" |
|||
|
|||
log "github.com/Sirupsen/logrus" |
|||
"github.com/matrix-org/go-neb/types" |
|||
"github.com/matrix-org/gomatrix" |
|||
) |
|||
|
|||
// ServiceType of the Mtgcard service.
|
|||
const ServiceType = "mtgcard" |
|||
|
|||
// Matches [[ then word separated with spaces then ]]. E.g "[[card Name]]
|
|||
var cardRegex = regexp.MustCompile(`\[\[\w+(\s*\w+)*\]\]`) |
|||
|
|||
type scryfallSearch struct { |
|||
Name string `json:"name"` |
|||
ScryfallURI string `json:"scryfall_uri"` |
|||
Euro string `json:"eur"` |
|||
PurchaseURIs struct { |
|||
Cardmarket string `json:cardmarket` |
|||
} `json:purchase_uris` |
|||
ImageURIs struct { |
|||
Normal string `json:"normal"` |
|||
Small string `json:"small"` |
|||
} `json:"image_uris"` |
|||
} |
|||
|
|||
// Service contains the Config fields for the Mtgcard Service.
|
|||
type Service struct { |
|||
types.DefaultService |
|||
} |
|||
|
|||
// Commands supported:
|
|||
// !mtgcard some search query without quotes
|
|||
// Responds with card information into the same room as the command.
|
|||
func (s *Service) Commands(client *gomatrix.Client) []types.Command { |
|||
return []types.Command{ |
|||
types.Command{ |
|||
Path: []string{"mtgcard"}, |
|||
Command: func(roomID, userID string, args []string) (interface{}, error) { |
|||
return s.cmdScryfall(client, roomID, userID, args) |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
|
|||
// Expansions expands card mentions represented as:
|
|||
// [[ possible name of card ]]
|
|||
// and responds with card information into the same room as the expansion
|
|||
func (s *Service) Expansions(cli *gomatrix.Client) []types.Expansion { |
|||
return []types.Expansion{ |
|||
types.Expansion{ |
|||
Regexp: cardRegex, |
|||
Expand: func(roomID, userID string, cardMentions []string) interface{} { |
|||
return s.expandCard(roomID, userID, cardMentions) |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
|
|||
// cmdScryfall processes a command by calling the query and returning a message with card info
|
|||
func (s *Service) cmdScryfall(client *gomatrix.Client, roomID, userID string, args []string) (interface{}, error) { |
|||
// only 1 arg which is the text to search for.
|
|||
query := strings.Join(args, " ") |
|||
|
|||
scryfallResult, err := s.searchScryfall(query) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
if scryfallResult != nil { |
|||
return createMessage(scryfallResult), err |
|||
} |
|||
return nil, err |
|||
|
|||
} |
|||
|
|||
// expandCard processes an expansion by calling the query and returning a message with card info
|
|||
func (s *Service) expandCard(roomID, userID string, cardMentions []string) interface{} { |
|||
// cardMentions => ["[[magic card]]"]
|
|||
|
|||
logger := log.WithField("cardMentions", cardMentions) |
|||
logger.WithFields(log.Fields{ |
|||
"room_id": roomID, |
|||
"user_id": userID, |
|||
}).Print("Expanding card mention") |
|||
|
|||
cardMentions[0] = strings.TrimPrefix(cardMentions[0], "[[") |
|||
cardMentions[0] = strings.TrimSuffix(cardMentions[0], "]]") |
|||
scryfallResult, err := s.searchScryfall(cardMentions[0]) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if scryfallResult != nil { |
|||
return createMessage(scryfallResult) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
// createMessage returns a nicely formatted matrix.HTMLMessage from a query result
|
|||
func createMessage(result *scryfallSearch) gomatrix.HTMLMessage { |
|||
|
|||
var htmlBuffer bytes.Buffer |
|||
var plainBuffer bytes.Buffer |
|||
message := fmt.Sprintf( |
|||
"<ul><li><a href=%s>%s</a>\t<a href=%s>(SF)</a>\t<a href=%s>(%s€)</a></li></ul>", |
|||
result.ImageURIs.Normal, |
|||
html.EscapeString(result.Name), |
|||
result.ScryfallURI, |
|||
result.PurchaseURIs.Cardmarket, |
|||
result.Euro, |
|||
) |
|||
htmlBuffer.WriteString(message) |
|||
plainBuffer.WriteString(fmt.Sprintf("$s, $s, $s", result.Name, result.ScryfallURI, result.Euro)) |
|||
|
|||
return gomatrix.HTMLMessage{ |
|||
Body: plainBuffer.String(), |
|||
MsgType: "m.notice", |
|||
Format: "org.matrix.custom.html", |
|||
FormattedBody: htmlBuffer.String(), |
|||
} |
|||
} |
|||
|
|||
// searchScryfall queries Scryfall API for card info
|
|||
func (s *Service) searchScryfall(query string) (*scryfallSearch, error) { |
|||
log.Info("Searching scryfall for ", query) |
|||
u, err := url.Parse("https://api.scryfall.com/cards/named") |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
q := u.Query() |
|||
q.Set("fuzzy", query) |
|||
u.RawQuery = q.Encode() |
|||
res, err := http.Get(u.String()) |
|||
if res != nil { |
|||
defer res.Body.Close() |
|||
} |
|||
if err != nil { |
|||
log.Error("Problem searching scryfall: ", err) |
|||
return nil, err |
|||
} |
|||
if res.StatusCode == http.StatusNotFound { |
|||
return nil, nil |
|||
} |
|||
var search scryfallSearch |
|||
if err := json.NewDecoder(res.Body).Decode(&search); err != nil { |
|||
return nil, err |
|||
} |
|||
log.Info("Search scryfall returned ", search) |
|||
return &search, nil |
|||
} |
|||
|
|||
func init() { |
|||
types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service { |
|||
return &Service{ |
|||
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType), |
|||
} |
|||
}) |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue