commit
2ef9a27726
20 changed files with 486 additions and 3103 deletions
17
Makefile
17
Makefile
|
@ -1,26 +1,23 @@
|
|||
full: web build
|
||||
|
||||
build:
|
||||
go build
|
||||
|
||||
full: build_web build_swagger build
|
||||
|
||||
full_run: full run
|
||||
|
||||
run: build
|
||||
./justlog
|
||||
|
||||
run_web:
|
||||
cd web && yarn start
|
||||
|
||||
build_swagger:
|
||||
swag init
|
||||
|
||||
build_web: get_web
|
||||
web: init_web
|
||||
cd web && yarn build
|
||||
go run api/assets.go
|
||||
|
||||
get_web:
|
||||
init_web:
|
||||
cd web && yarn install
|
||||
|
||||
init_assets:
|
||||
go run api/assets.go
|
||||
|
||||
# Docker stuff
|
||||
container:
|
||||
docker build -t gempir/justlog .
|
||||
|
|
File diff suppressed because one or more lines are too long
142
api/channel.go
142
api/channel.go
|
@ -1,116 +1,38 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gempir/go-twitch-irc/v2"
|
||||
"github.com/labstack/echo/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (s *Server) getCurrentChannelLogs(c echo.Context) error {
|
||||
channelID := c.Param("channelid")
|
||||
year := time.Now().Year()
|
||||
month := int(time.Now().Month())
|
||||
day := time.Now().Day()
|
||||
|
||||
redirectURL := fmt.Sprintf("/channelid/%s/%d/%d/%d", channelID, year, month, day)
|
||||
if len(c.QueryString()) > 0 {
|
||||
redirectURL += "?" + c.QueryString()
|
||||
}
|
||||
return c.Redirect(http.StatusSeeOther, redirectURL)
|
||||
}
|
||||
|
||||
func (s *Server) getCurrentChannelLogsByName(c echo.Context) error {
|
||||
channel := c.Param("channel")
|
||||
year := time.Now().Year()
|
||||
month := int(time.Now().Month())
|
||||
day := time.Now().Day()
|
||||
|
||||
redirectURL := fmt.Sprintf("/channel/%s/%d/%d/%d", channel, year, month, day)
|
||||
if len(c.QueryString()) > 0 {
|
||||
redirectURL += "?" + c.QueryString()
|
||||
}
|
||||
return c.Redirect(http.StatusSeeOther, redirectURL)
|
||||
}
|
||||
|
||||
func (s *Server) getChannelLogsByName(c echo.Context) error {
|
||||
channel := strings.ToLower(c.Param("channel"))
|
||||
|
||||
userMap, err := s.helixClient.GetUsersByUsernames([]string{channel})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, "Failure fetching data from twitch")
|
||||
}
|
||||
|
||||
names := c.ParamNames()
|
||||
names = append(names, "channelid")
|
||||
|
||||
values := c.ParamValues()
|
||||
values = append(values, userMap[channel].ID)
|
||||
|
||||
c.SetParamNames(names...)
|
||||
c.SetParamValues(values...)
|
||||
|
||||
return s.getChannelLogs(c)
|
||||
}
|
||||
|
||||
func (s *Server) getChannelLogsRangeByName(c echo.Context) error {
|
||||
channel := strings.ToLower(c.Param("channel"))
|
||||
|
||||
userMap, err := s.helixClient.GetUsersByUsernames([]string{channel})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, "Failure fetching data from twitch")
|
||||
}
|
||||
|
||||
names := c.ParamNames()
|
||||
names = append(names, "channelid")
|
||||
|
||||
values := c.ParamValues()
|
||||
values = append(values, userMap[channel].ID)
|
||||
|
||||
c.SetParamNames(names...)
|
||||
c.SetParamValues(values...)
|
||||
|
||||
return s.getChannelLogsRange(c)
|
||||
}
|
||||
|
||||
func (s *Server) getChannelLogs(c echo.Context) error {
|
||||
channelID := c.Param("channelid")
|
||||
|
||||
yearStr := c.Param("year")
|
||||
monthStr := c.Param("month")
|
||||
dayStr := c.Param("day")
|
||||
func (s *Server) getChannelLogs(request logRequest) (*chatLog, error) {
|
||||
yearStr := request.time.year
|
||||
monthStr := request.time.month
|
||||
dayStr := request.time.day
|
||||
|
||||
year, err := strconv.Atoi(yearStr)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, "Invalid year")
|
||||
return &chatLog{}, errors.New("invalid year")
|
||||
}
|
||||
month, err := strconv.Atoi(monthStr)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, "Invalid month")
|
||||
return &chatLog{}, errors.New("invalid month")
|
||||
}
|
||||
day, err := strconv.Atoi(dayStr)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, "Invalid day")
|
||||
return &chatLog{}, errors.New("invalid day")
|
||||
}
|
||||
|
||||
logMessages, err := s.fileLogger.ReadLogForChannel(channelID, year, month, day)
|
||||
logMessages, err := s.fileLogger.ReadLogForChannel(request.channelid, year, month, day)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusNotFound, "Failure reading log")
|
||||
return &chatLog{}, err
|
||||
}
|
||||
|
||||
if shouldReverse(c) {
|
||||
reverse(logMessages)
|
||||
if request.reverse {
|
||||
reverseSlice(logMessages)
|
||||
}
|
||||
|
||||
var logResult chatLog
|
||||
|
@ -169,41 +91,27 @@ func (s *Server) getChannelLogs(c echo.Context) error {
|
|||
logResult.Messages = append(logResult.Messages, chatMsg)
|
||||
}
|
||||
|
||||
if shouldRespondWithJson(c) {
|
||||
return writeJSONResponse(c, &logResult)
|
||||
}
|
||||
|
||||
if shouldRespondWithRaw(c) {
|
||||
return writeRawResponse(c, &logResult)
|
||||
}
|
||||
|
||||
return writeTextResponse(c, &logResult)
|
||||
return &logResult, nil
|
||||
}
|
||||
|
||||
func (s *Server) getChannelLogsRange(c echo.Context) error {
|
||||
channelID := c.Param("channelid")
|
||||
|
||||
fromTime, toTime, err := parseFromTo(c.QueryParam("from"), c.QueryParam("to"), channelHourLimit)
|
||||
func (s *Server) getChannelLogsRange(request logRequest) (*chatLog, error) {
|
||||
fromTime, toTime, err := parseFromTo(request.time.from, request.time.to, userHourLimit)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return &chatLog{}, err
|
||||
}
|
||||
|
||||
var logMessages []string
|
||||
|
||||
logMessages, _ = s.fileLogger.ReadLogForChannel(channelID, fromTime.Year(), int(fromTime.Month()), fromTime.Day())
|
||||
logMessages, _ = s.fileLogger.ReadLogForChannel(request.channelid, fromTime.Year(), int(fromTime.Month()), fromTime.Day())
|
||||
|
||||
if fromTime.Month() != toTime.Month() {
|
||||
additionalMessages, _ := s.fileLogger.ReadLogForChannel(channelID, toTime.Year(), int(toTime.Month()), toTime.Day())
|
||||
additionalMessages, _ := s.fileLogger.ReadLogForChannel(request.channelid, toTime.Year(), int(toTime.Month()), toTime.Day())
|
||||
|
||||
logMessages = append(logMessages, additionalMessages...)
|
||||
}
|
||||
|
||||
if len(logMessages) == 0 {
|
||||
return c.JSON(http.StatusNotFound, "No logs found")
|
||||
}
|
||||
|
||||
if shouldReverse(c) {
|
||||
reverse(logMessages)
|
||||
if request.reverse {
|
||||
reverseSlice(logMessages)
|
||||
}
|
||||
|
||||
var logResult chatLog
|
||||
|
@ -270,13 +178,5 @@ func (s *Server) getChannelLogsRange(c echo.Context) error {
|
|||
logResult.Messages = append(logResult.Messages, chatMsg)
|
||||
}
|
||||
|
||||
if shouldRespondWithJson(c) {
|
||||
return writeJSONResponse(c, &logResult)
|
||||
}
|
||||
|
||||
if shouldRespondWithRaw(c) {
|
||||
return writeRawResponse(c, &logResult)
|
||||
}
|
||||
|
||||
return writeTextResponse(c, &logResult)
|
||||
return &logResult, nil
|
||||
}
|
||||
|
|
163
api/logrequest.go
Normal file
163
api/logrequest.go
Normal file
|
@ -0,0 +1,163 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type logRequest struct {
|
||||
channel string
|
||||
user string
|
||||
channelid string
|
||||
userid string
|
||||
time logTime
|
||||
reverse bool
|
||||
responseType string
|
||||
redirectPath string
|
||||
isUserRequest bool
|
||||
isChannelRequest bool
|
||||
}
|
||||
|
||||
// userRandomMessageRequest /channel/pajlada/user/gempir/random
|
||||
|
||||
type logTime struct {
|
||||
from string
|
||||
to string
|
||||
year string
|
||||
month string
|
||||
day string
|
||||
random bool
|
||||
}
|
||||
|
||||
var (
|
||||
pathRegex = regexp.MustCompile(`\/(channel|channelid)\/([a-zA-Z0-9]+)(?:\/(user|userid)\/([a-zA-Z0-9]+))?(?:(?:\/(\d{4})\/(\d{1,2})(?:\/(\d{1,2}))?)|(?:\/(range|random)))?`)
|
||||
)
|
||||
|
||||
func (s *Server) newLogRequestFromURL(r *http.Request) (logRequest, error) {
|
||||
path := r.URL.EscapedPath()
|
||||
|
||||
if !strings.HasPrefix(path, "/channel") && !strings.HasPrefix(path, "/channelid") {
|
||||
return logRequest{}, errors.New("route not found")
|
||||
}
|
||||
|
||||
url := strings.TrimRight(path, "/")
|
||||
|
||||
matches := pathRegex.FindAllStringSubmatch(url, -1)
|
||||
if len(matches) == 0 || len(matches[0]) < 5 {
|
||||
return logRequest{}, errors.New("route not found")
|
||||
}
|
||||
|
||||
request := logRequest{
|
||||
time: logTime{},
|
||||
}
|
||||
|
||||
params := []string{}
|
||||
for _, match := range matches[0] {
|
||||
if match != "" {
|
||||
params = append(params, match)
|
||||
}
|
||||
}
|
||||
|
||||
request.isUserRequest = len(params) > 4 && (params[3] == "user" || params[3] == "userid")
|
||||
request.isChannelRequest = len(params) < 4 || (len(params) >= 4 && params[3] != "user" && params[3] != "userid")
|
||||
|
||||
if params[1] == "channel" {
|
||||
request.channel = params[2]
|
||||
}
|
||||
if params[1] == "channelid" {
|
||||
request.channelid = params[2]
|
||||
}
|
||||
if request.isUserRequest && params[3] == "user" {
|
||||
request.user = params[4]
|
||||
}
|
||||
if request.isUserRequest && params[3] == "userid" {
|
||||
request.userid = params[4]
|
||||
}
|
||||
|
||||
if request.isUserRequest && len(params) == 7 {
|
||||
request.time.year = params[5]
|
||||
request.time.month = params[6]
|
||||
} else if request.isChannelRequest && len(params) == 6 {
|
||||
request.time.year = params[3]
|
||||
request.time.month = params[4]
|
||||
request.time.day = params[5]
|
||||
} else if request.isUserRequest && len(params) == 6 && params[5] == "random" {
|
||||
request.time.random = true
|
||||
} else if (request.isUserRequest && len(params) == 6 && params[5] == "range") || (request.isChannelRequest && len(params) == 4 && params[3] == "range") {
|
||||
request.time.from = r.URL.Query().Get("from")
|
||||
request.time.to = r.URL.Query().Get("to")
|
||||
} else {
|
||||
request.time.year = fmt.Sprintf("%d", time.Now().Year())
|
||||
request.time.month = fmt.Sprintf("%d", time.Now().Month())
|
||||
timePath := request.time.year + "/" + request.time.month
|
||||
|
||||
if request.isChannelRequest {
|
||||
request.time.day = fmt.Sprintf("%d", time.Now().Day())
|
||||
timePath += "/" + request.time.day
|
||||
}
|
||||
|
||||
query := r.URL.Query()
|
||||
query.Del("from")
|
||||
query.Del("to")
|
||||
|
||||
encodedQuery := ""
|
||||
if query.Encode() != "" {
|
||||
encodedQuery = "?" + query.Encode()
|
||||
}
|
||||
|
||||
return logRequest{redirectPath: fmt.Sprintf("%s/%s%s", params[0], timePath, encodedQuery)}, nil
|
||||
}
|
||||
|
||||
if _, ok := r.URL.Query()["reverse"]; ok {
|
||||
request.reverse = true
|
||||
} else {
|
||||
request.reverse = false
|
||||
}
|
||||
|
||||
if _, ok := r.URL.Query()["json"]; ok || r.URL.Query().Get("type") == "json" || r.Header.Get("Content-Type") == "application/json" {
|
||||
request.responseType = responseTypeJSON
|
||||
} else if _, ok := r.URL.Query()["raw"]; ok || r.URL.Query().Get("type") == "raw" {
|
||||
request.responseType = responseTypeRaw
|
||||
} else {
|
||||
request.responseType = responseTypeText
|
||||
}
|
||||
|
||||
var err error
|
||||
request, err = s.fillIds(request)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return logRequest{}, nil
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (s *Server) fillIds(request logRequest) (logRequest, error) {
|
||||
usernames := []string{}
|
||||
if request.channelid == "" && request.channel != "" {
|
||||
usernames = append(usernames, request.channel)
|
||||
}
|
||||
if request.userid == "" && request.user != "" {
|
||||
usernames = append(usernames, request.user)
|
||||
}
|
||||
|
||||
ids, err := s.helixClient.GetUsersByUsernames(usernames)
|
||||
if err != nil {
|
||||
return request, err
|
||||
}
|
||||
|
||||
if request.channelid == "" {
|
||||
request.channelid = ids[request.channel].ID
|
||||
}
|
||||
if request.userid == "" {
|
||||
request.userid = ids[request.user].ID
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
293
api/server.go
293
api/server.go
|
@ -14,22 +14,20 @@ import (
|
|||
|
||||
"github.com/gempir/go-twitch-irc/v2"
|
||||
"github.com/gempir/justlog/filelog"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
|
||||
_ "github.com/gempir/justlog/docs"
|
||||
echoSwagger "github.com/swaggo/echo-swagger"
|
||||
)
|
||||
|
||||
// Server api server
|
||||
type Server struct {
|
||||
listenAddress string
|
||||
logPath string
|
||||
fileLogger *filelog.Logger
|
||||
helixClient *helix.Client
|
||||
channels []string
|
||||
assets []string
|
||||
assetHandler http.Handler
|
||||
}
|
||||
|
||||
// NewServer create api Server
|
||||
func NewServer(logPath string, listenAddress string, fileLogger *filelog.Logger, helixClient *helix.Client, channels []string) Server {
|
||||
return Server{
|
||||
listenAddress: listenAddress,
|
||||
|
@ -37,67 +35,21 @@ func NewServer(logPath string, listenAddress string, fileLogger *filelog.Logger,
|
|||
fileLogger: fileLogger,
|
||||
helixClient: helixClient,
|
||||
channels: channels,
|
||||
assets: []string{"/", "/bundle.js", "/favicon.ico"},
|
||||
assetHandler: http.FileServer(assets),
|
||||
}
|
||||
}
|
||||
|
||||
// AddChannel adds a channel to the collection to output on the channels endpoint
|
||||
func (s *Server) AddChannel(channel string) {
|
||||
s.channels = append(s.channels, channel)
|
||||
}
|
||||
|
||||
func (s *Server) Init() {
|
||||
e := echo.New()
|
||||
e.HideBanner = true
|
||||
|
||||
DefaultCORSConfig := middleware.CORSConfig{
|
||||
Skipper: middleware.DefaultSkipper,
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE},
|
||||
}
|
||||
e.Use(middleware.RemoveTrailingSlashWithConfig(middleware.TrailingSlashConfig{
|
||||
RedirectCode: http.StatusMovedPermanently,
|
||||
}))
|
||||
e.Use(middleware.SecureWithConfig(middleware.SecureConfig{
|
||||
XSSProtection: "", // disabled
|
||||
ContentTypeNosniff: "nosniff",
|
||||
XFrameOptions: "", // disabled
|
||||
HSTSMaxAge: 0, // disabled
|
||||
ContentSecurityPolicy: "", // disabled
|
||||
}))
|
||||
e.Use(middleware.CORSWithConfig(DefaultCORSConfig))
|
||||
|
||||
assetHandler := http.FileServer(assets)
|
||||
e.GET("/", echo.WrapHandler(assetHandler))
|
||||
e.GET("/bundle.js", echo.WrapHandler(assetHandler))
|
||||
|
||||
e.GET("/docs", func(c echo.Context) error {
|
||||
return c.Redirect(http.StatusMovedPermanently, "/swagger/index.html")
|
||||
})
|
||||
e.GET("/swagger/*", echoSwagger.WrapHandler)
|
||||
e.GET("/channels", s.getAllChannels)
|
||||
|
||||
e.GET("/channel/:channel/user/:username/range", s.getUserLogsRangeByName)
|
||||
e.GET("/channelid/:channelid/userid/:userid/range", s.getUserLogsRange)
|
||||
|
||||
e.GET("/channel/:channel/user/:username", s.getLastUserLogsByName)
|
||||
e.GET("/channel/:channel/user/:username/:year/:month", s.getUserLogsByName)
|
||||
e.GET("/channel/:channel/user/:username/random", s.getRandomQuoteByName)
|
||||
|
||||
e.GET("/channelid/:channelid/userid/:userid", s.getLastUserLogs)
|
||||
e.GET("/channelid/:channelid/userid/:userid/:year/:month", s.getUserLogs)
|
||||
e.GET("/channelid/:channelid/userid/:userid/random", s.getRandomQuote)
|
||||
|
||||
e.GET("/v2/:channelType/:channel/:userType/:user/:year/:month", s.getUserLogsExact)
|
||||
|
||||
e.GET("/channelid/:channelid/range", s.getChannelLogsRange)
|
||||
e.GET("/channel/:channel/range", s.getChannelLogsRangeByName)
|
||||
|
||||
e.GET("/channel/:channel", s.getCurrentChannelLogsByName)
|
||||
e.GET("/channel/:channel/:year/:month/:day", s.getChannelLogsByName)
|
||||
e.GET("/channelid/:channelid", s.getCurrentChannelLogs)
|
||||
e.GET("/channelid/:channelid/:year/:month/:day", s.getChannelLogs)
|
||||
|
||||
e.Logger.Fatal(e.Start(s.listenAddress))
|
||||
}
|
||||
const (
|
||||
responseTypeJSON = "json"
|
||||
responseTypeText = "text"
|
||||
responseTypeRaw = "raw"
|
||||
)
|
||||
|
||||
var (
|
||||
userHourLimit = 744.0
|
||||
|
@ -109,6 +61,7 @@ type channel struct {
|
|||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// AllChannelsJSON inlcudes all channels
|
||||
type AllChannelsJSON struct {
|
||||
Channels []channel `json:"channels"`
|
||||
}
|
||||
|
@ -127,6 +80,7 @@ type chatMessage struct {
|
|||
Raw string `json:"raw"`
|
||||
}
|
||||
|
||||
// ErrorResponse a simple error response
|
||||
type ErrorResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
@ -135,35 +89,165 @@ type timestamp struct {
|
|||
time.Time
|
||||
}
|
||||
|
||||
func reverse(input []string) []string {
|
||||
// Init start the server
|
||||
func (s *Server) Init() {
|
||||
http.Handle("/", corsHandler(http.HandlerFunc(s.route)))
|
||||
|
||||
log.Fatal(http.ListenAndServe(s.listenAddress, nil))
|
||||
}
|
||||
|
||||
func (s *Server) route(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.URL.EscapedPath()
|
||||
|
||||
if contains(s.assets, url) {
|
||||
s.assetHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if url == "/channels" {
|
||||
s.getAllChannels(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
s.routeLogs(w, r)
|
||||
}
|
||||
|
||||
func (s *Server) routeLogs(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
request, err := s.newLogRequestFromURL(r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if request.redirectPath != "" {
|
||||
http.Redirect(w, r, request.redirectPath, http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
var logs *chatLog
|
||||
if request.time.random {
|
||||
logs, err = s.getRandomQuote(request)
|
||||
} else if request.time.from != "" && request.time.to != "" {
|
||||
if request.isUserRequest {
|
||||
logs, err = s.getUserLogsRange(request)
|
||||
} else {
|
||||
logs, err = s.getChannelLogsRange(request)
|
||||
}
|
||||
|
||||
} else {
|
||||
if request.isUserRequest {
|
||||
logs, err = s.getUserLogs(request)
|
||||
} else {
|
||||
logs, err = s.getChannelLogs(request)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
http.Error(w, "could not load logs", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if request.responseType == responseTypeJSON {
|
||||
writeJSON(logs, http.StatusOK, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if request.responseType == responseTypeRaw {
|
||||
writeRaw(logs, http.StatusOK, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if request.responseType == responseTypeText {
|
||||
writeText(logs, http.StatusOK, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "unkown response type", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func corsHandler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
} else {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
h.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func contains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func reverseSlice(input []string) []string {
|
||||
for i, j := 0, len(input)-1; i < j; i, j = i+1, j-1 {
|
||||
input[i], input[j] = input[j], input[i]
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
// getAllChannels godoc
|
||||
// @Summary Get all joined channels
|
||||
// @tags bot
|
||||
// @Produce json
|
||||
// @Success 200 {object} api.RandomQuoteJSON json
|
||||
// @Failure 500 {object} api.ErrorResponse json
|
||||
// @Router /channels [get]
|
||||
func (s *Server) getAllChannels(c echo.Context) error {
|
||||
func (s *Server) getAllChannels(w http.ResponseWriter, r *http.Request) {
|
||||
response := new(AllChannelsJSON)
|
||||
response.Channels = []channel{}
|
||||
users, err := s.helixClient.GetUsersByUserIds(s.channels)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{"Failure fetching data from twitch"})
|
||||
http.Error(w, "Failure fetching data from twitch", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
response.Channels = append(response.Channels, channel{UserID: user.ID, Name: user.Login})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, response)
|
||||
writeJSON(response, http.StatusOK, w, r)
|
||||
}
|
||||
|
||||
func writeJSON(data interface{}, code int, w http.ResponseWriter, r *http.Request) {
|
||||
js, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
func writeRaw(cLog *chatLog, code int, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(code)
|
||||
|
||||
for _, cMessage := range cLog.Messages {
|
||||
w.Write([]byte(cMessage.Raw + "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func writeText(cLog *chatLog, code int, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(code)
|
||||
|
||||
for _, cMessage := range cLog.Messages {
|
||||
switch cMessage.Type {
|
||||
case twitch.PRIVMSG:
|
||||
w.Write([]byte(fmt.Sprintf("[%s] #%s %s: %s\n", cMessage.Timestamp.Format("2006-01-2 15:04:05"), cMessage.Channel, cMessage.Username, cMessage.Text)))
|
||||
case twitch.CLEARCHAT:
|
||||
w.Write([]byte(fmt.Sprintf("[%s] #%s %s\n", cMessage.Timestamp.Format("2006-01-2 15:04:05"), cMessage.Channel, cMessage.Text)))
|
||||
case twitch.USERNOTICE:
|
||||
w.Write([]byte(fmt.Sprintf("[%s] #%s %s\n", cMessage.Timestamp.Format("2006-01-2 15:04:05"), cMessage.Channel, cMessage.Text)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t timestamp) MarshalJSON() ([]byte, error) {
|
||||
|
@ -222,53 +306,6 @@ func parseFromTo(from, to string, limit float64) (time.Time, time.Time, error) {
|
|||
return fromTime, toTime, nil
|
||||
}
|
||||
|
||||
func writeTextResponse(c echo.Context, cLog *chatLog) error {
|
||||
c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextPlainCharsetUTF8)
|
||||
c.Response().WriteHeader(http.StatusOK)
|
||||
|
||||
for _, cMessage := range cLog.Messages {
|
||||
switch cMessage.Type {
|
||||
case twitch.PRIVMSG:
|
||||
c.Response().Write([]byte(fmt.Sprintf("[%s] #%s %s: %s\n", cMessage.Timestamp.Format("2006-01-2 15:04:05"), cMessage.Channel, cMessage.Username, cMessage.Text)))
|
||||
case twitch.CLEARCHAT:
|
||||
c.Response().Write([]byte(fmt.Sprintf("[%s] #%s %s\n", cMessage.Timestamp.Format("2006-01-2 15:04:05"), cMessage.Channel, cMessage.Text)))
|
||||
case twitch.USERNOTICE:
|
||||
c.Response().Write([]byte(fmt.Sprintf("[%s] #%s %s\n", cMessage.Timestamp.Format("2006-01-2 15:04:05"), cMessage.Channel, cMessage.Text)))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeRawResponse(c echo.Context, cLog *chatLog) error {
|
||||
c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextPlainCharsetUTF8)
|
||||
c.Response().WriteHeader(http.StatusOK)
|
||||
|
||||
for _, cMessage := range cLog.Messages {
|
||||
c.Response().Write([]byte(cMessage.Raw + "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeJSONResponse(c echo.Context, logResult *chatLog) error {
|
||||
_, stream := c.QueryParams()["stream"]
|
||||
if stream {
|
||||
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
|
||||
c.Response().WriteHeader(http.StatusOK)
|
||||
|
||||
return json.NewEncoder(c.Response()).Encode(logResult)
|
||||
}
|
||||
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
data, err := json.Marshal(logResult)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return c.Blob(http.StatusOK, echo.MIMEApplicationJSONCharsetUTF8, data)
|
||||
}
|
||||
|
||||
func parseTimestamp(timestamp string) (time.Time, error) {
|
||||
|
||||
i, err := strconv.ParseInt(timestamp, 10, 64)
|
||||
|
@ -278,20 +315,10 @@ func parseTimestamp(timestamp string) (time.Time, error) {
|
|||
return time.Unix(i, 0), nil
|
||||
}
|
||||
|
||||
func shouldReverse(c echo.Context) bool {
|
||||
_, ok := c.QueryParams()["reverse"]
|
||||
func buildClearChatMessageText(message twitch.ClearChatMessage) string {
|
||||
if message.BanDuration == 0 {
|
||||
return fmt.Sprintf("%s has been banned", message.TargetUsername)
|
||||
}
|
||||
|
||||
return c.QueryParam("order") == "reverse" || ok
|
||||
}
|
||||
|
||||
func shouldRespondWithJson(c echo.Context) bool {
|
||||
_, ok := c.QueryParams()["json"]
|
||||
|
||||
return c.Request().Header.Get("Content-Type") == "application/json" || c.Request().Header.Get("accept") == "application/json" || c.QueryParam("type") == "json" || ok
|
||||
}
|
||||
|
||||
func shouldRespondWithRaw(c echo.Context) bool {
|
||||
_, ok := c.QueryParams()["raw"]
|
||||
|
||||
return c.QueryParam("type") == "raw" || ok
|
||||
return fmt.Sprintf("%s has been timed out for %d seconds", message.TargetUsername, message.BanDuration)
|
||||
}
|
||||
|
|
393
api/user.go
393
api/user.go
|
@ -2,16 +2,11 @@ package api
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gempir/go-twitch-irc/v2"
|
||||
"github.com/labstack/echo/v4"
|
||||
helix "github.com/gempir/justlog/helix"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// RandomQuoteJSON response when request a random message
|
||||
type RandomQuoteJSON struct {
|
||||
Channel string `json:"channel"`
|
||||
Username string `json:"username"`
|
||||
|
@ -20,328 +15,64 @@ type RandomQuoteJSON struct {
|
|||
Timestamp timestamp `json:"timestamp"`
|
||||
}
|
||||
|
||||
// @Summary Redirect to last logs of user
|
||||
// @tags user
|
||||
// @Produce json
|
||||
// @Produce plain
|
||||
// @Param channelid path string true "twitch userid"
|
||||
// @Param userid path string true "twitch userid"
|
||||
// @Param from query int false "unix timestamp, limit logs by timestamps from this point"
|
||||
// @Param to query int false "unix timestamp, limit logs by timestamps to this point"
|
||||
// @Param json query int false "response as json"
|
||||
// @Param type query string false "define response type only json supported currently, rest defaults to plain"
|
||||
// @Success 303
|
||||
// @Success 200
|
||||
// @Failure 404
|
||||
// @Router /channelid/{channelid}/userid/{userid} [get]
|
||||
func (s *Server) getLastUserLogs(c echo.Context) error {
|
||||
channelID := c.Param("channelid")
|
||||
userID := c.Param("userid")
|
||||
|
||||
year, month, err := s.fileLogger.GetLastLogYearAndMonthForUser(channelID, userID)
|
||||
func (s *Server) getRandomQuote(request logRequest) (*chatLog, error) {
|
||||
rawMessage, err := s.fileLogger.ReadRandomMessageForUser(request.channelid, request.userid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusNotFound, ErrorResponse{"No logs found"})
|
||||
}
|
||||
|
||||
redirectURL := fmt.Sprintf("/channelid/%s/userid/%s/%d/%d", channelID, userID, year, month)
|
||||
if len(c.QueryString()) > 0 {
|
||||
redirectURL += "?" + c.QueryString()
|
||||
}
|
||||
return c.Redirect(303, redirectURL)
|
||||
}
|
||||
|
||||
// @Summary Redirect to last logs of user
|
||||
// @tags user
|
||||
// @Produce json
|
||||
// @Produce plain
|
||||
// @Param channel path string true "channelname"
|
||||
// @Param username path string true "username"
|
||||
// @Param from query int false "unix timestamp, limit logs by timestamps from this point"
|
||||
// @Param to query int false "unix timestamp, limit logs by timestamps to this point"
|
||||
// @Param json query int false "response as json"
|
||||
// @Param type query string false "define response type only json supported currently, rest defaults to plain"
|
||||
// @Success 303
|
||||
// @Success 200
|
||||
// @Failure 500
|
||||
// @Failure 404
|
||||
// @Router /channel/{channel}/user/{username} [get]
|
||||
func (s *Server) getLastUserLogsByName(c echo.Context) error {
|
||||
channel := strings.ToLower(c.Param("channel"))
|
||||
username := strings.ToLower(c.Param("username"))
|
||||
|
||||
userMap, err := s.helixClient.GetUsersByUsernames([]string{channel, username})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{"Failure fetching data from twitch"})
|
||||
}
|
||||
var year int
|
||||
var month int
|
||||
year, month, err = s.fileLogger.GetLastLogYearAndMonthForUser(userMap[channel].ID, userMap[username].ID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusNotFound, ErrorResponse{"No logs found"})
|
||||
}
|
||||
|
||||
redirectURL := fmt.Sprintf("/channel/%s/user/%s/%d/%d", channel, username, year, month)
|
||||
if len(c.QueryString()) > 0 {
|
||||
redirectURL += "?" + c.QueryString()
|
||||
}
|
||||
return c.Redirect(303, redirectURL)
|
||||
}
|
||||
|
||||
|
||||
// @Summary UNSTABLE DO NOT USE
|
||||
// @tags user
|
||||
// @Deprecated
|
||||
// @Produce json
|
||||
// @Produce plain
|
||||
// @Param channelType path string true "id or name"
|
||||
// @Param userIdType path string true "id or name"
|
||||
// @Param channel path string true "channelid or channelname"
|
||||
// @Param user path string true "userid or username"
|
||||
// @Param year path string true "year of logs"
|
||||
// @Param month path string true "month of logs"
|
||||
// @Param from query int false "unix timestamp, limit logs by timestamps from this point"
|
||||
// @Param to query int false "unix timestamp, limit logs by timestamps to this point"
|
||||
// @Param json query int false "response as json"
|
||||
// @Param type query string false "define response type only json supported currently, rest defaults to plain"
|
||||
// @Success 200
|
||||
// @Failure 500
|
||||
// @Router /v2/{channelType}/{channel}/{userType}/{user}/{year}/{month} [get]
|
||||
func (s *Server) getUserLogsExact(c echo.Context) error {
|
||||
channel := strings.ToLower(c.Param("channel"))
|
||||
user := strings.ToLower(c.Param("user"))
|
||||
|
||||
userMap := map[string]helix.UserData{}
|
||||
if (c.Param("channelType") == "name" || c.Param("userType") == "name") {
|
||||
var err error
|
||||
userMap, err = s.helixClient.GetUsersByUsernames([]string{channel, user})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{"Failure fetching data from twitch"})
|
||||
}
|
||||
}
|
||||
|
||||
names := c.ParamNames()
|
||||
values := c.ParamValues()
|
||||
names = append(names, "channelid")
|
||||
|
||||
if (c.Param("channelType") == "name") {
|
||||
values = append(values, userMap[channel].ID)
|
||||
} else {
|
||||
values = append(values, c.Param("channel"))
|
||||
}
|
||||
|
||||
names = append(names, "userid")
|
||||
if (c.Param("userType") == "name") {
|
||||
values = append(values, userMap[user].ID)
|
||||
} else {
|
||||
values = append(values, c.Param("user"))
|
||||
}
|
||||
|
||||
c.SetParamNames(names...)
|
||||
c.SetParamValues(values...)
|
||||
|
||||
return s.getUserLogs(c)
|
||||
}
|
||||
|
||||
func (s *Server) getUserLogsRangeByName(c echo.Context) error {
|
||||
channel := strings.ToLower(c.Param("channel"))
|
||||
username := strings.ToLower(c.Param("username"))
|
||||
|
||||
userMap, err := s.helixClient.GetUsersByUsernames([]string{channel, username})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{"Failure fetching data from twitch"})
|
||||
}
|
||||
|
||||
names := c.ParamNames()
|
||||
names = append(names, "channelid")
|
||||
names = append(names, "userid")
|
||||
|
||||
values := c.ParamValues()
|
||||
values = append(values, userMap[channel].ID)
|
||||
values = append(values, userMap[username].ID)
|
||||
|
||||
c.SetParamNames(names...)
|
||||
c.SetParamValues(values...)
|
||||
|
||||
return s.getUserLogsRange(c)
|
||||
}
|
||||
|
||||
// @Summary Get logs for user by year and month
|
||||
// @tags user
|
||||
// @Produce json
|
||||
// @Produce plain
|
||||
// @Param channel path string true "channelname"
|
||||
// @Param username path string true "username"
|
||||
// @Param year path string true "year of logs"
|
||||
// @Param month path string true "month of logs"
|
||||
// @Param from query int false "unix timestamp, limit logs by timestamps from this point"
|
||||
// @Param to query int false "unix timestamp, limit logs by timestamps to this point"
|
||||
// @Param json query int false "response as json"
|
||||
// @Param type query string false "define response type only json supported currently, rest defaults to plain"
|
||||
// @Success 200
|
||||
// @Failure 500
|
||||
// @Router /channel/{channel}/user/{username}/{year}/{month} [get]
|
||||
func (s *Server) getUserLogsByName(c echo.Context) error {
|
||||
channel := strings.ToLower(c.Param("channel"))
|
||||
username := strings.ToLower(c.Param("username"))
|
||||
|
||||
userMap, err := s.helixClient.GetUsersByUsernames([]string{channel, username})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{"Failure fetching data from twitch"})
|
||||
}
|
||||
|
||||
names := c.ParamNames()
|
||||
names = append(names, "channelid")
|
||||
names = append(names, "userid")
|
||||
|
||||
values := c.ParamValues()
|
||||
values = append(values, userMap[channel].ID)
|
||||
values = append(values, userMap[username].ID)
|
||||
|
||||
c.SetParamNames(names...)
|
||||
c.SetParamValues(values...)
|
||||
|
||||
return s.getUserLogs(c)
|
||||
}
|
||||
|
||||
// @Summary Get a random chat message from a user
|
||||
// @tags user
|
||||
// @Produce json
|
||||
// @Produce plain
|
||||
// @Param channel path string true "channelname"
|
||||
// @Param username path string true "username"
|
||||
// @Param json query int false "response as json"
|
||||
// @Param type query string false "define response type only json supported currently, rest defaults to plain"
|
||||
// @Success 200 {object} api.RandomQuoteJSON json
|
||||
// @Router /channel/{channel}/user/{username}/random [get]
|
||||
func (s *Server) getRandomQuoteByName(c echo.Context) error {
|
||||
channel := strings.ToLower(c.Param("channel"))
|
||||
username := strings.ToLower(c.Param("username"))
|
||||
|
||||
userMap, err := s.helixClient.GetUsersByUsernames([]string{channel, username})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{"Failure fetching data from twitch"})
|
||||
}
|
||||
|
||||
names := c.ParamNames()
|
||||
names = append(names, "channelid")
|
||||
names = append(names, "userid")
|
||||
|
||||
values := c.ParamValues()
|
||||
values = append(values, userMap[channel].ID)
|
||||
values = append(values, userMap[username].ID)
|
||||
|
||||
c.SetParamNames(names...)
|
||||
c.SetParamValues(values...)
|
||||
|
||||
return s.getRandomQuote(c)
|
||||
}
|
||||
|
||||
// @Summary Get a random chat message from a user
|
||||
// @tags user
|
||||
// @Produce json
|
||||
// @Produce plain
|
||||
// @Param channelid path string true "twitch userid"
|
||||
// @Param userid path string true "twitch userid"
|
||||
// @Param json query int false "response as json"
|
||||
// @Param type query string false "define response type only json supported currently, rest defaults to plain"
|
||||
// @Success 200 {object} api.RandomQuoteJSON json
|
||||
// @Router /channelid/{channelid}/userid/{userid}/random [get]
|
||||
func (s *Server) getRandomQuote(c echo.Context) error {
|
||||
channelID := c.Param("channelid")
|
||||
userID := c.Param("userid")
|
||||
|
||||
rawMessage, err := s.fileLogger.ReadRandomMessageForUser(channelID, userID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return &chatLog{}, err
|
||||
}
|
||||
parsedMessage := twitch.ParseMessage(rawMessage)
|
||||
|
||||
var chatMsg chatMessage
|
||||
switch parsedMessage.(type) {
|
||||
case *twitch.PrivateMessage:
|
||||
message := *parsedMessage.(*twitch.PrivateMessage)
|
||||
|
||||
if shouldRespondWithJson(c) {
|
||||
|
||||
randomQ := RandomQuoteJSON{
|
||||
Channel: message.Channel,
|
||||
Username: message.User.Name,
|
||||
DisplayName: message.User.DisplayName,
|
||||
Message: message.Message,
|
||||
Timestamp: timestamp{message.Time},
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, randomQ)
|
||||
chatMsg = chatMessage{
|
||||
Timestamp: timestamp{message.Time},
|
||||
Username: message.User.Name,
|
||||
DisplayName: message.User.DisplayName,
|
||||
Text: message.Message,
|
||||
Type: message.Type,
|
||||
Channel: message.Channel,
|
||||
Raw: message.Raw,
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, message.Message)
|
||||
case *twitch.ClearChatMessage:
|
||||
message := *parsedMessage.(*twitch.ClearChatMessage)
|
||||
|
||||
if shouldRespondWithJson(c) {
|
||||
|
||||
randomQ := RandomQuoteJSON{
|
||||
Channel: message.Channel,
|
||||
Username: message.TargetUsername,
|
||||
DisplayName: message.TargetUsername,
|
||||
Message: buildClearChatMessageText(message),
|
||||
Timestamp: timestamp{message.Time},
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, randomQ)
|
||||
chatMsg = chatMessage{
|
||||
Timestamp: timestamp{message.Time},
|
||||
Username: message.TargetUsername,
|
||||
DisplayName: message.TargetUsername,
|
||||
Text: buildClearChatMessageText(message),
|
||||
Type: message.Type,
|
||||
Channel: message.Channel,
|
||||
Raw: message.Raw,
|
||||
}
|
||||
case *twitch.UserNoticeMessage:
|
||||
message := *parsedMessage.(*twitch.UserNoticeMessage)
|
||||
|
||||
return c.String(http.StatusOK, buildClearChatMessageText(message))
|
||||
chatMsg = chatMessage{
|
||||
Timestamp: timestamp{message.Time},
|
||||
Username: message.User.Name,
|
||||
DisplayName: message.User.DisplayName,
|
||||
Text: message.SystemMsg + " " + message.Message,
|
||||
Type: message.Type,
|
||||
Channel: message.Channel,
|
||||
Raw: message.Raw,
|
||||
}
|
||||
}
|
||||
|
||||
return c.String(http.StatusNotFound, "No quote found")
|
||||
return &chatLog{Messages: []chatMessage{chatMsg}}, nil
|
||||
}
|
||||
|
||||
// @Summary Get logs for user by year and month
|
||||
// @tags user
|
||||
// @Produce json
|
||||
// @Produce plain
|
||||
// @Param channelid path string true "twitch userid"
|
||||
// @Param userid path string true "twitch userid"
|
||||
// @Param year path string true "year of logs"
|
||||
// @Param month path string true "month of logs"
|
||||
// @Param from query int false "unix timestamp, limit logs by timestamps from this point"
|
||||
// @Param to query int false "unix timestamp, limit logs by timestamps to this point"
|
||||
// @Param json query int false "response as json"
|
||||
// @Param type query string false "define response type only json supported currently, rest defaults to plain"
|
||||
// @Success 200
|
||||
// @Failure 500
|
||||
// @Router /channelid/{channelid}/userid/{userid}/{year}/{month} [get]
|
||||
func (s *Server) getUserLogs(c echo.Context) error {
|
||||
channelID := c.Param("channelid")
|
||||
userID := c.Param("userid")
|
||||
|
||||
yearStr := c.Param("year")
|
||||
monthStr := c.Param("month")
|
||||
|
||||
year, err := strconv.Atoi(yearStr)
|
||||
func (s *Server) getUserLogs(request logRequest) (*chatLog, error) {
|
||||
logMessages, err := s.fileLogger.ReadLogForUser(request.channelid, request.userid, request.time.year, request.time.month)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{"Invalid year"})
|
||||
}
|
||||
month, err := strconv.Atoi(monthStr)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{"Invalid month"})
|
||||
return &chatLog{}, err
|
||||
}
|
||||
|
||||
logMessages, err := s.fileLogger.ReadLogForUser(channelID, userID, year, month)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return c.JSON(http.StatusNotFound, ErrorResponse{"Failure reading log"})
|
||||
}
|
||||
|
||||
if shouldReverse(c) {
|
||||
reverse(logMessages)
|
||||
if request.reverse {
|
||||
reverseSlice(logMessages)
|
||||
}
|
||||
|
||||
var logResult chatLog
|
||||
|
@ -393,42 +124,28 @@ func (s *Server) getUserLogs(c echo.Context) error {
|
|||
logResult.Messages = append(logResult.Messages, chatMsg)
|
||||
}
|
||||
|
||||
if shouldRespondWithJson(c) {
|
||||
return writeJSONResponse(c, &logResult)
|
||||
}
|
||||
|
||||
if shouldRespondWithRaw(c) {
|
||||
return writeRawResponse(c, &logResult)
|
||||
}
|
||||
|
||||
return writeTextResponse(c, &logResult)
|
||||
return &logResult, nil
|
||||
}
|
||||
|
||||
func (s *Server) getUserLogsRange(c echo.Context) error {
|
||||
channelID := c.Param("channelid")
|
||||
userID := c.Param("userid")
|
||||
func (s *Server) getUserLogsRange(request logRequest) (*chatLog, error) {
|
||||
|
||||
fromTime, toTime, err := parseFromTo(c.QueryParam("from"), c.QueryParam("to"), userHourLimit)
|
||||
fromTime, toTime, err := parseFromTo(request.time.from, request.time.to, userHourLimit)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{err.Error()})
|
||||
return &chatLog{}, err
|
||||
}
|
||||
|
||||
var logMessages []string
|
||||
|
||||
logMessages, _ = s.fileLogger.ReadLogForUser(channelID, userID, fromTime.Year(), int(fromTime.Month()))
|
||||
logMessages, _ = s.fileLogger.ReadLogForUser(request.channelid, request.userid, fmt.Sprintf("%d", fromTime.Year()), fmt.Sprintf("%d", int(fromTime.Month())))
|
||||
|
||||
if fromTime.Month() != toTime.Month() {
|
||||
additionalMessages, _ := s.fileLogger.ReadLogForUser(channelID, userID, toTime.Year(), int(toTime.Month()))
|
||||
additionalMessages, _ := s.fileLogger.ReadLogForUser(request.channelid, request.userid, string(toTime.Year()), string(toTime.Month()))
|
||||
|
||||
logMessages = append(logMessages, additionalMessages...)
|
||||
}
|
||||
|
||||
if len(logMessages) == 0 {
|
||||
return c.JSON(http.StatusNotFound, ErrorResponse{"No logs found"})
|
||||
}
|
||||
|
||||
if shouldReverse(c) {
|
||||
reverse(logMessages)
|
||||
if request.reverse {
|
||||
reverseSlice(logMessages)
|
||||
}
|
||||
|
||||
var logResult chatLog
|
||||
|
@ -488,21 +205,5 @@ func (s *Server) getUserLogsRange(c echo.Context) error {
|
|||
logResult.Messages = append(logResult.Messages, chatMsg)
|
||||
}
|
||||
|
||||
if shouldRespondWithJson(c) {
|
||||
return writeJSONResponse(c, &logResult)
|
||||
}
|
||||
|
||||
if shouldRespondWithRaw(c) {
|
||||
return writeRawResponse(c, &logResult)
|
||||
}
|
||||
|
||||
return writeTextResponse(c, &logResult)
|
||||
}
|
||||
|
||||
func buildClearChatMessageText(message twitch.ClearChatMessage) string {
|
||||
if message.BanDuration == 0 {
|
||||
return fmt.Sprintf("%s has been banned", message.TargetUsername)
|
||||
} else {
|
||||
return fmt.Sprintf("%s has been timed out for %d seconds", message.TargetUsername, message.BanDuration)
|
||||
}
|
||||
return &logResult, nil
|
||||
}
|
||||
|
|
579
docs/docs.go
579
docs/docs.go
|
@ -1,579 +0,0 @@
|
|||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
// This file was generated by swaggo/swag at
|
||||
// 2020-02-11 18:50:01.55539 +0100 CET m=+0.121127201
|
||||
|
||||
package docs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/template"
|
||||
"github.com/swaggo/swag"
|
||||
)
|
||||
|
||||
var doc = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{.Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"contact": {
|
||||
"name": "gempir",
|
||||
"url": "https://gempir.com",
|
||||
"email": "gempir.dev@gmail.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/gempir/justlog/blob/master/LICENSE"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/channel/{channel}/user/{username}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Redirect to last logs of user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"303": {},
|
||||
"404": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channel/{channel}/user/{username}/random": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get a random chat message from a user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.RandomQuoteJSON"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channel/{channel}/user/{username}/{year}/{month}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get logs for user by year and month",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "year of logs",
|
||||
"name": "year",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "month of logs",
|
||||
"name": "month",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channelid/{channelid}/userid/{userid}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Redirect to last logs of user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "channelid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "userid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"303": {},
|
||||
"404": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channelid/{channelid}/userid/{userid}/random": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get a random chat message from a user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "channelid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "userid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.RandomQuoteJSON"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channelid/{channelid}/userid/{userid}/{year}/{month}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get logs for user by year and month",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "channelid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "userid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "year of logs",
|
||||
"name": "year",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "month of logs",
|
||||
"name": "month",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channels": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"bot"
|
||||
],
|
||||
"summary": "Get all joined channels",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.RandomQuoteJSON"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/{channelType}/{channel}/{userType}/{user}/{year}/{month}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "UNSTABLE DO NOT USE",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "id or name",
|
||||
"name": "channelType",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "id or name",
|
||||
"name": "userIdType",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelid or channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "userid or username",
|
||||
"name": "user",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "year of logs",
|
||||
"name": "year",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "month of logs",
|
||||
"name": "month",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.RandomQuoteJSON": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"displayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/api.timestamp"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.timestamp": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
type swaggerInfo struct {
|
||||
Version string
|
||||
Host string
|
||||
BasePath string
|
||||
Schemes []string
|
||||
Title string
|
||||
Description string
|
||||
}
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = swaggerInfo{
|
||||
Version: "1.0",
|
||||
Host: "logs.ivr.fi",
|
||||
BasePath: "/",
|
||||
Schemes: []string{},
|
||||
Title: "justlog API",
|
||||
Description: "API for twitch logs",
|
||||
}
|
||||
|
||||
type s struct{}
|
||||
|
||||
func (s *s) ReadDoc() string {
|
||||
sInfo := SwaggerInfo
|
||||
sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
|
||||
|
||||
t, err := template.New("swagger_info").Funcs(template.FuncMap{
|
||||
"marshal": func(v interface{}) string {
|
||||
a, _ := json.Marshal(v)
|
||||
return string(a)
|
||||
},
|
||||
}).Parse(doc)
|
||||
if err != nil {
|
||||
return doc
|
||||
}
|
||||
|
||||
var tpl bytes.Buffer
|
||||
if err := t.Execute(&tpl, sInfo); err != nil {
|
||||
return doc
|
||||
}
|
||||
|
||||
return tpl.String()
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(swag.Name, &s{})
|
||||
}
|
|
@ -1,516 +0,0 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "API for twitch logs",
|
||||
"title": "justlog API",
|
||||
"contact": {
|
||||
"name": "gempir",
|
||||
"url": "https://gempir.com",
|
||||
"email": "gempir.dev@gmail.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/gempir/justlog/blob/master/LICENSE"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "logs.ivr.fi",
|
||||
"basePath": "/",
|
||||
"paths": {
|
||||
"/channel/{channel}/user/{username}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Redirect to last logs of user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"303": {},
|
||||
"404": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channel/{channel}/user/{username}/random": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get a random chat message from a user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.RandomQuoteJSON"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channel/{channel}/user/{username}/{year}/{month}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get logs for user by year and month",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "year of logs",
|
||||
"name": "year",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "month of logs",
|
||||
"name": "month",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channelid/{channelid}/userid/{userid}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Redirect to last logs of user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "channelid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "userid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"303": {},
|
||||
"404": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channelid/{channelid}/userid/{userid}/random": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get a random chat message from a user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "channelid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "userid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.RandomQuoteJSON"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channelid/{channelid}/userid/{userid}/{year}/{month}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get logs for user by year and month",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "channelid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "userid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "year of logs",
|
||||
"name": "year",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "month of logs",
|
||||
"name": "month",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channels": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"bot"
|
||||
],
|
||||
"summary": "Get all joined channels",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.RandomQuoteJSON"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/{channelType}/{channel}/{userType}/{user}/{year}/{month}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "UNSTABLE DO NOT USE",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "id or name",
|
||||
"name": "channelType",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "id or name",
|
||||
"name": "userIdType",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelid or channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "userid or username",
|
||||
"name": "user",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "year of logs",
|
||||
"name": "year",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "month of logs",
|
||||
"name": "month",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.RandomQuoteJSON": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"displayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/api.timestamp"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.timestamp": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,358 +0,0 @@
|
|||
basePath: /
|
||||
definitions:
|
||||
api.ErrorResponse:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
api.RandomQuoteJSON:
|
||||
properties:
|
||||
channel:
|
||||
type: string
|
||||
displayName:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
timestamp:
|
||||
$ref: '#/definitions/api.timestamp'
|
||||
type: object
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
api.timestamp:
|
||||
type: object
|
||||
host: logs.ivr.fi
|
||||
info:
|
||||
contact:
|
||||
email: gempir.dev@gmail.com
|
||||
name: gempir
|
||||
url: https://gempir.com
|
||||
description: API for twitch logs
|
||||
license:
|
||||
name: MIT
|
||||
url: https://github.com/gempir/justlog/blob/master/LICENSE
|
||||
title: justlog API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/channel/{channel}/user/{username}:
|
||||
get:
|
||||
parameters:
|
||||
- description: channelname
|
||||
in: path
|
||||
name: channel
|
||||
required: true
|
||||
type: string
|
||||
- description: username
|
||||
in: path
|
||||
name: username
|
||||
required: true
|
||||
type: string
|
||||
- description: unix timestamp, limit logs by timestamps from this point
|
||||
in: query
|
||||
name: from
|
||||
type: integer
|
||||
- description: unix timestamp, limit logs by timestamps to this point
|
||||
in: query
|
||||
name: to
|
||||
type: integer
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: integer
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200": {}
|
||||
"303": {}
|
||||
"404": {}
|
||||
"500": {}
|
||||
summary: Redirect to last logs of user
|
||||
tags:
|
||||
- user
|
||||
/channel/{channel}/user/{username}/{year}/{month}:
|
||||
get:
|
||||
parameters:
|
||||
- description: channelname
|
||||
in: path
|
||||
name: channel
|
||||
required: true
|
||||
type: string
|
||||
- description: username
|
||||
in: path
|
||||
name: username
|
||||
required: true
|
||||
type: string
|
||||
- description: year of logs
|
||||
in: path
|
||||
name: year
|
||||
required: true
|
||||
type: string
|
||||
- description: month of logs
|
||||
in: path
|
||||
name: month
|
||||
required: true
|
||||
type: string
|
||||
- description: unix timestamp, limit logs by timestamps from this point
|
||||
in: query
|
||||
name: from
|
||||
type: integer
|
||||
- description: unix timestamp, limit logs by timestamps to this point
|
||||
in: query
|
||||
name: to
|
||||
type: integer
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: integer
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200": {}
|
||||
"500": {}
|
||||
summary: Get logs for user by year and month
|
||||
tags:
|
||||
- user
|
||||
/channel/{channel}/user/{username}/random:
|
||||
get:
|
||||
parameters:
|
||||
- description: channelname
|
||||
in: path
|
||||
name: channel
|
||||
required: true
|
||||
type: string
|
||||
- description: username
|
||||
in: path
|
||||
name: username
|
||||
required: true
|
||||
type: string
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: integer
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.RandomQuoteJSON'
|
||||
summary: Get a random chat message from a user
|
||||
tags:
|
||||
- user
|
||||
/channelid/{channelid}/userid/{userid}:
|
||||
get:
|
||||
parameters:
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: channelid
|
||||
required: true
|
||||
type: string
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: userid
|
||||
required: true
|
||||
type: string
|
||||
- description: unix timestamp, limit logs by timestamps from this point
|
||||
in: query
|
||||
name: from
|
||||
type: integer
|
||||
- description: unix timestamp, limit logs by timestamps to this point
|
||||
in: query
|
||||
name: to
|
||||
type: integer
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: integer
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200": {}
|
||||
"303": {}
|
||||
"404": {}
|
||||
summary: Redirect to last logs of user
|
||||
tags:
|
||||
- user
|
||||
/channelid/{channelid}/userid/{userid}/{year}/{month}:
|
||||
get:
|
||||
parameters:
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: channelid
|
||||
required: true
|
||||
type: string
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: userid
|
||||
required: true
|
||||
type: string
|
||||
- description: year of logs
|
||||
in: path
|
||||
name: year
|
||||
required: true
|
||||
type: string
|
||||
- description: month of logs
|
||||
in: path
|
||||
name: month
|
||||
required: true
|
||||
type: string
|
||||
- description: unix timestamp, limit logs by timestamps from this point
|
||||
in: query
|
||||
name: from
|
||||
type: integer
|
||||
- description: unix timestamp, limit logs by timestamps to this point
|
||||
in: query
|
||||
name: to
|
||||
type: integer
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: integer
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200": {}
|
||||
"500": {}
|
||||
summary: Get logs for user by year and month
|
||||
tags:
|
||||
- user
|
||||
/channelid/{channelid}/userid/{userid}/random:
|
||||
get:
|
||||
parameters:
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: channelid
|
||||
required: true
|
||||
type: string
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: userid
|
||||
required: true
|
||||
type: string
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: integer
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.RandomQuoteJSON'
|
||||
summary: Get a random chat message from a user
|
||||
tags:
|
||||
- user
|
||||
/channels:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.RandomQuoteJSON'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrorResponse'
|
||||
summary: Get all joined channels
|
||||
tags:
|
||||
- bot
|
||||
/v2/{channelType}/{channel}/{userType}/{user}/{year}/{month}:
|
||||
get:
|
||||
deprecated: true
|
||||
parameters:
|
||||
- description: id or name
|
||||
in: path
|
||||
name: channelType
|
||||
required: true
|
||||
type: string
|
||||
- description: id or name
|
||||
in: path
|
||||
name: userIdType
|
||||
required: true
|
||||
type: string
|
||||
- description: channelid or channelname
|
||||
in: path
|
||||
name: channel
|
||||
required: true
|
||||
type: string
|
||||
- description: userid or username
|
||||
in: path
|
||||
name: user
|
||||
required: true
|
||||
type: string
|
||||
- description: year of logs
|
||||
in: path
|
||||
name: year
|
||||
required: true
|
||||
type: string
|
||||
- description: month of logs
|
||||
in: path
|
||||
name: month
|
||||
required: true
|
||||
type: string
|
||||
- description: unix timestamp, limit logs by timestamps from this point
|
||||
in: query
|
||||
name: from
|
||||
type: integer
|
||||
- description: unix timestamp, limit logs by timestamps to this point
|
||||
in: query
|
||||
name: to
|
||||
type: integer
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: integer
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200": {}
|
||||
"500": {}
|
||||
summary: UNSTABLE DO NOT USE
|
||||
tags:
|
||||
- user
|
||||
swagger: "2.0"
|
|
@ -1,435 +0,0 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "API for twitch logs",
|
||||
"title": "justlog API",
|
||||
"contact": {
|
||||
"name": "gempir",
|
||||
"url": "https://gempir.com",
|
||||
"email": "gempir.dev@gmail.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/gempir/justlog/blob/master/LICENSE"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "api.gempir.com",
|
||||
"basePath": "/",
|
||||
"paths": {
|
||||
"/channel/{channel}/user/{username}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Redirect to last logs of user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "any",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"303": {},
|
||||
"404": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channel/{channel}/user/{username}/random": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get a random chat message from a user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "any",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/api.RandomQuoteJSON"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channel/{channel}/user/{username}/{year}/{month}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get logs for user by year and month",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "channelname",
|
||||
"name": "channel",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "year of logs",
|
||||
"name": "year",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "month of logs",
|
||||
"name": "month",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "any",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channelid/{channelid}/user/{userid}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Redirect to last logs of user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "channelid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "userid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "any",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"303": {},
|
||||
"404": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channelid/{channelid}/userid/{userid}/random": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get a random chat message from a user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "channelid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "userid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "any",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/api.RandomQuoteJSON"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channelid/{channelid}/userid/{userid}/{year}/{month}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json",
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get logs for user by year and month",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "channelid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "twitch userid",
|
||||
"name": "userid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "year of logs",
|
||||
"name": "year",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "month of logs",
|
||||
"name": "month",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps from this point",
|
||||
"name": "from",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "unix timestamp, limit logs by timestamps to this point",
|
||||
"name": "to",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "any",
|
||||
"description": "response as json",
|
||||
"name": "json",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "define response type only json supported currently, rest defaults to plain",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {},
|
||||
"500": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/channels": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"bot"
|
||||
],
|
||||
"summary": "Get all joined channels",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/api.RandomQuoteJSON"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/api.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.RandomQuoteJSON": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"displayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/api.timestamp"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.timestamp": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,302 +0,0 @@
|
|||
basePath: /
|
||||
definitions:
|
||||
api.ErrorResponse:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
api.RandomQuoteJSON:
|
||||
properties:
|
||||
channel:
|
||||
type: string
|
||||
displayName:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
timestamp:
|
||||
$ref: '#/definitions/api.timestamp'
|
||||
type: object
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
api.timestamp:
|
||||
type: object
|
||||
host: api.gempir.com
|
||||
info:
|
||||
contact:
|
||||
email: gempir.dev@gmail.com
|
||||
name: gempir
|
||||
url: https://gempir.com
|
||||
description: API for twitch logs
|
||||
license:
|
||||
name: MIT
|
||||
url: https://github.com/gempir/justlog/blob/master/LICENSE
|
||||
title: justlog API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/channel/{channel}/user/{username}:
|
||||
get:
|
||||
parameters:
|
||||
- description: channelname
|
||||
in: path
|
||||
name: channel
|
||||
required: true
|
||||
type: string
|
||||
- description: username
|
||||
in: path
|
||||
name: username
|
||||
required: true
|
||||
type: string
|
||||
- description: unix timestamp, limit logs by timestamps from this point
|
||||
in: query
|
||||
name: from
|
||||
type: integer
|
||||
- description: unix timestamp, limit logs by timestamps to this point
|
||||
in: query
|
||||
name: to
|
||||
type: integer
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: any
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200": {}
|
||||
"303": {}
|
||||
"404": {}
|
||||
"500": {}
|
||||
summary: Redirect to last logs of user
|
||||
tags:
|
||||
- user
|
||||
/channel/{channel}/user/{username}/{year}/{month}:
|
||||
get:
|
||||
parameters:
|
||||
- description: channelname
|
||||
in: path
|
||||
name: channel
|
||||
required: true
|
||||
type: string
|
||||
- description: username
|
||||
in: path
|
||||
name: username
|
||||
required: true
|
||||
type: string
|
||||
- description: year of logs
|
||||
in: path
|
||||
name: year
|
||||
required: true
|
||||
type: string
|
||||
- description: month of logs
|
||||
in: path
|
||||
name: month
|
||||
required: true
|
||||
type: string
|
||||
- description: unix timestamp, limit logs by timestamps from this point
|
||||
in: query
|
||||
name: from
|
||||
type: integer
|
||||
- description: unix timestamp, limit logs by timestamps to this point
|
||||
in: query
|
||||
name: to
|
||||
type: integer
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: any
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200": {}
|
||||
"500": {}
|
||||
summary: Get logs for user by year and month
|
||||
tags:
|
||||
- user
|
||||
/channel/{channel}/user/{username}/random:
|
||||
get:
|
||||
parameters:
|
||||
- description: channelname
|
||||
in: path
|
||||
name: channel
|
||||
required: true
|
||||
type: string
|
||||
- description: username
|
||||
in: path
|
||||
name: username
|
||||
required: true
|
||||
type: string
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: any
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.RandomQuoteJSON'
|
||||
type: object
|
||||
summary: Get a random chat message from a user
|
||||
tags:
|
||||
- user
|
||||
/channelid/{channelid}/user/{userid}:
|
||||
get:
|
||||
parameters:
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: channelid
|
||||
required: true
|
||||
type: string
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: userid
|
||||
required: true
|
||||
type: string
|
||||
- description: unix timestamp, limit logs by timestamps from this point
|
||||
in: query
|
||||
name: from
|
||||
type: integer
|
||||
- description: unix timestamp, limit logs by timestamps to this point
|
||||
in: query
|
||||
name: to
|
||||
type: integer
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: any
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200": {}
|
||||
"303": {}
|
||||
"404": {}
|
||||
summary: Redirect to last logs of user
|
||||
tags:
|
||||
- user
|
||||
/channelid/{channelid}/userid/{userid}/{year}/{month}:
|
||||
get:
|
||||
parameters:
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: channelid
|
||||
required: true
|
||||
type: string
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: userid
|
||||
required: true
|
||||
type: string
|
||||
- description: year of logs
|
||||
in: path
|
||||
name: year
|
||||
required: true
|
||||
type: string
|
||||
- description: month of logs
|
||||
in: path
|
||||
name: month
|
||||
required: true
|
||||
type: string
|
||||
- description: unix timestamp, limit logs by timestamps from this point
|
||||
in: query
|
||||
name: from
|
||||
type: integer
|
||||
- description: unix timestamp, limit logs by timestamps to this point
|
||||
in: query
|
||||
name: to
|
||||
type: integer
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: any
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200": {}
|
||||
"500": {}
|
||||
summary: Get logs for user by year and month
|
||||
tags:
|
||||
- user
|
||||
/channelid/{channelid}/userid/{userid}/random:
|
||||
get:
|
||||
parameters:
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: channelid
|
||||
required: true
|
||||
type: string
|
||||
- description: twitch userid
|
||||
in: path
|
||||
name: userid
|
||||
required: true
|
||||
type: string
|
||||
- description: response as json
|
||||
in: query
|
||||
name: json
|
||||
type: any
|
||||
- description: define response type only json supported currently, rest defaults
|
||||
to plain
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/plain
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.RandomQuoteJSON'
|
||||
type: object
|
||||
summary: Get a random chat message from a user
|
||||
tags:
|
||||
- user
|
||||
/channels:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.RandomQuoteJSON'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrorResponse'
|
||||
type: object
|
||||
summary: Get all joined channels
|
||||
tags:
|
||||
- bot
|
||||
swagger: "2.0"
|
|
@ -148,12 +148,13 @@ func (l *Logger) GetLastLogYearAndMonthForUser(channelID, userID string) (int, i
|
|||
return 0, 0, errors.New("No logs file")
|
||||
}
|
||||
|
||||
func (l *Logger) ReadLogForUser(channelID, userID string, year int, month int) ([]string, error) {
|
||||
// ReadLogForUser fetch logs
|
||||
func (l *Logger) ReadLogForUser(channelID, userID string, year string, month string) ([]string, error) {
|
||||
if channelID == "" || userID == "" {
|
||||
return []string{}, fmt.Errorf("Invalid channelID: %s or userID: %s", channelID, userID)
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf(l.logPath+"/%s/%d/%d/%s.txt", channelID, year, month, userID)
|
||||
filename := fmt.Sprintf(l.logPath+"/%s/%s/%s/%s.txt", channelID, year, month, userID)
|
||||
|
||||
if _, err := os.Stat(filename); err != nil {
|
||||
filename = filename + ".gz"
|
||||
|
|
18
go.mod
18
go.mod
|
@ -3,20 +3,12 @@ module github.com/gempir/justlog
|
|||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/gempir/go-twitch-irc/v2 v2.2.0
|
||||
github.com/go-openapi/spec v0.19.6 // indirect
|
||||
github.com/go-openapi/swag v0.19.7 // indirect
|
||||
github.com/json-iterator/go v1.1.7
|
||||
github.com/labstack/echo/v4 v4.0.0
|
||||
github.com/mailru/easyjson v0.7.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/nicklaw5/helix v0.5.7
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/swaggo/echo-swagger v0.0.0-20190329130007-1219b460a043
|
||||
github.com/swaggo/swag v1.6.5
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
|
||||
golang.org/x/tools v0.0.0-20200211045251-2de505fc5306 // indirect
|
||||
github.com/stretchr/testify v1.4.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
)
|
||||
|
|
189
go.sum
189
go.sum
|
@ -1,217 +1,34 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/gempir/go-twitch-irc/v2 v2.2.0 h1:9iYRr/PkT5tqnD9J0awBXtwS4R4DatA5cMQbsua6OvM=
|
||||
github.com/gempir/go-twitch-irc/v2 v2.2.0/go.mod h1:0HXoEr9l7gNjwajosptV0w0xGpHeU6gsD7JDlfvjTYI=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0 h1:KVRzjXpMzgdM4GEMDmDTnGcY5yBwGWreJwmmk4k35yU=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=
|
||||
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
|
||||
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
|
||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/spec v0.19.6 h1:rMMMj8cV38KVXK7SFc+I2MWClbEfbK705+j+dyqun5g=
|
||||
github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0 h1:1DU8Km1MRGv9Pj7BNLmkA+umwTStwDHttXvx3NhJA70=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.6 h1:JSUbVWlaTLMhXeOMyArSUNCdroxZu2j1TcrsOV8Mj7Q=
|
||||
github.com/go-openapi/swag v0.19.6/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
|
||||
github.com/go-openapi/swag v0.19.7 h1:VRuXN2EnMSsZdauzdss6JBC29YotDqG59BZ+tdlIL1s=
|
||||
github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labstack/echo/v4 v4.0.0 h1:q1GH+caIXPP7H2StPIdzy/ez9CO0EepqYeUg6vi9SWM=
|
||||
github.com/labstack/echo/v4 v4.0.0/go.mod h1:tZv7nai5buKSg5h/8E6zz4LsD/Dqh9/91Mvs7Z5Zyno=
|
||||
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
|
||||
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/nicklaw5/helix v0.5.7 h1:DvNyoKkuLYrqZv5/yugL18Ud99UeQoXzzAsg4OwU8uY=
|
||||
github.com/nicklaw5/helix v0.5.7/go.mod h1:nRcok4VLg8ONQYW/iXBZ24wcfiJjTlDbhgk0ZatOrUY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/swaggo/echo-swagger v0.0.0-20190329130007-1219b460a043 h1:OAfyh6btUpExwOwroWsVem3XMlQrxONtQUMtglckrPo=
|
||||
github.com/swaggo/echo-swagger v0.0.0-20190329130007-1219b460a043/go.mod h1:XxhCMHL5pDVR8YSHWhc4duJmH4T1eV5CYD5IaYPxFzg=
|
||||
github.com/swaggo/files v0.0.0-20190110041405-30649e0721f8 h1:ENF9W2s6+pqe/CmdQTQFPuzSdCB91LQ3WWzdMWucs7c=
|
||||
github.com/swaggo/files v0.0.0-20190110041405-30649e0721f8/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
|
||||
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=
|
||||
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
|
||||
github.com/swaggo/gin-swagger v1.0.0 h1:k6Nn1jV49u+SNIWt7kejQS/iENZKZVMCNQrKOYatNF8=
|
||||
github.com/swaggo/gin-swagger v1.0.0/go.mod h1:Mt37wE46iUaTAOv+HSnHbJYssKGqbS25X19lNF4YpBo=
|
||||
github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=
|
||||
github.com/swaggo/swag v1.4.0/go.mod h1:hog2WgeMOrQ/LvQ+o1YGTeT+vWVrbi0SiIslBtxKTyM=
|
||||
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
|
||||
github.com/swaggo/swag v1.6.2 h1:WQMAtT/FmMBb7g0rAuHDhG3vvdtHKJ3WZ+Ssb0p4Y6E=
|
||||
github.com/swaggo/swag v1.6.2/go.mod h1:YyZstMc22WYm6GEDx/CYWxq+faBbjQ5EqwQcrjREDBo=
|
||||
github.com/swaggo/swag v1.6.5 h1:2C+t+xyK6p1sujqncYO/VnMvPZcBJjNdKKyxbOdAW8o=
|
||||
github.com/swaggo/swag v1.6.5/go.mod h1:Y7ZLSS0d0DdxhWGVhQdu+Bu1QhaF5k0RD7FKdiAykeY=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
|
||||
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
|
||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190205050122-7f7074d5bcfd/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468 h1:fTfk6GjmihJbK0mSUFgPPgYpsdmApQ86Mcd4GuKax9U=
|
||||
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 h1:QjA/9ArTfVTLfEhClDCG7SGrZkZixxWpwNCDiwJfh88=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20200125223703-d33eef8e6825 h1:aNQeSIHKi0RWpKA5NO0CqyLjx6Beh5l0LLUEnndEjz0=
|
||||
golang.org/x/tools v0.0.0-20200125223703-d33eef8e6825/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200211045251-2de505fc5306 h1:5gd/+xxg4X7hx+44aG6Sdh17vBwwRFacMaSfqF4wkWk=
|
||||
golang.org/x/tools v0.0.0-20200211045251-2de505fc5306/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
140
helix/user.go
140
helix/user.go
|
@ -1,32 +1,41 @@
|
|||
package helix
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
helixClient "github.com/nicklaw5/helix"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Client wrapper for helix
|
||||
type Client struct {
|
||||
clientID string
|
||||
client *helixClient.Client
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
var (
|
||||
userCacheByID map[string]UserData
|
||||
userCacheByUsername map[string]UserData
|
||||
userCacheByID map[string]*UserData
|
||||
userCacheByUsername map[string]*UserData
|
||||
)
|
||||
|
||||
func init() {
|
||||
userCacheByID = map[string]UserData{}
|
||||
userCacheByUsername = map[string]UserData{}
|
||||
userCacheByID = map[string]*UserData{}
|
||||
userCacheByUsername = map[string]*UserData{}
|
||||
}
|
||||
|
||||
// NewClient Create helix client
|
||||
func NewClient(clientID string) Client {
|
||||
client, err := helixClient.NewClient(&helixClient.Options{
|
||||
ClientID: clientID,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return Client{
|
||||
clientID: clientID,
|
||||
client: client,
|
||||
httpClient: &http.Client{},
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +44,7 @@ type userResponse struct {
|
|||
Data []UserData `json:"data"`
|
||||
}
|
||||
|
||||
// UserData exported data from twitch
|
||||
type UserData struct {
|
||||
ID string `json:"id"`
|
||||
Login string `json:"login"`
|
||||
|
@ -48,6 +58,7 @@ type UserData struct {
|
|||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// GetUsersByUserIds receive userData for given ids
|
||||
func (c *Client) GetUsersByUserIds(userIDs []string) (map[string]UserData, error) {
|
||||
var filteredUserIDs []string
|
||||
|
||||
|
@ -57,40 +68,44 @@ func (c *Client) GetUsersByUserIds(userIDs []string) (map[string]UserData, error
|
|||
}
|
||||
}
|
||||
|
||||
if len(filteredUserIDs) == 1 {
|
||||
params := "?id=" + filteredUserIDs[0]
|
||||
|
||||
err := c.makeRequest(params)
|
||||
if len(filteredUserIDs) > 0 {
|
||||
resp, err := c.client.GetUsers(&helixClient.UsersParams{
|
||||
IDs: filteredUserIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return map[string]UserData{}, err
|
||||
}
|
||||
|
||||
} else if len(filteredUserIDs) > 1 {
|
||||
var params string
|
||||
log.Infof("%d GetUsersByUserIds %v", resp.StatusCode, filteredUserIDs)
|
||||
|
||||
for index, id := range filteredUserIDs {
|
||||
if index == 0 {
|
||||
params += "?id=" + id
|
||||
} else {
|
||||
params += "&id=" + id
|
||||
for _, user := range resp.Data.Users {
|
||||
data := &UserData{
|
||||
ID: user.ID,
|
||||
Login: user.Login,
|
||||
DisplayName: user.Login,
|
||||
Type: user.Type,
|
||||
BroadcasterType: user.BroadcasterType,
|
||||
Description: user.Description,
|
||||
ProfileImageURL: user.ProfileImageURL,
|
||||
OfflineImageURL: user.OfflineImageURL,
|
||||
ViewCount: user.ViewCount,
|
||||
Email: user.Email,
|
||||
}
|
||||
}
|
||||
|
||||
err := c.makeRequest(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
userCacheByID[user.ID] = data
|
||||
userCacheByUsername[user.Login] = data
|
||||
}
|
||||
}
|
||||
|
||||
result := make(map[string]UserData)
|
||||
|
||||
for _, id := range userIDs {
|
||||
result[id] = userCacheByID[id]
|
||||
result[id] = *userCacheByID[id]
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetUsersByUsernames fetches userdata from helix
|
||||
func (c *Client) GetUsersByUsernames(usernames []string) (map[string]UserData, error) {
|
||||
var filteredUsernames []string
|
||||
|
||||
|
@ -100,72 +115,39 @@ func (c *Client) GetUsersByUsernames(usernames []string) (map[string]UserData, e
|
|||
}
|
||||
}
|
||||
|
||||
if len(filteredUsernames) == 1 {
|
||||
params := "?login=" + filteredUsernames[0]
|
||||
|
||||
err := c.makeRequest(params)
|
||||
if len(filteredUsernames) > 0 {
|
||||
resp, err := c.client.GetUsers(&helixClient.UsersParams{
|
||||
Logins: filteredUsernames,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return map[string]UserData{}, err
|
||||
}
|
||||
|
||||
} else if len(filteredUsernames) > 1 {
|
||||
var params string
|
||||
log.Infof("%d GetUsersByUsernames %v", resp.StatusCode, filteredUsernames)
|
||||
|
||||
for index, id := range filteredUsernames {
|
||||
if index == 0 {
|
||||
params += "?login=" + id
|
||||
} else {
|
||||
params += "&login=" + id
|
||||
for _, user := range resp.Data.Users {
|
||||
data := &UserData{
|
||||
ID: user.ID,
|
||||
Login: user.Login,
|
||||
DisplayName: user.Login,
|
||||
Type: user.Type,
|
||||
BroadcasterType: user.BroadcasterType,
|
||||
Description: user.Description,
|
||||
ProfileImageURL: user.ProfileImageURL,
|
||||
OfflineImageURL: user.OfflineImageURL,
|
||||
ViewCount: user.ViewCount,
|
||||
Email: user.Email,
|
||||
}
|
||||
}
|
||||
|
||||
err := c.makeRequest(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
userCacheByID[user.ID] = data
|
||||
userCacheByUsername[user.Login] = data
|
||||
}
|
||||
}
|
||||
|
||||
result := make(map[string]UserData)
|
||||
|
||||
for _, username := range usernames {
|
||||
result[username] = userCacheByUsername[username]
|
||||
result[username] = *userCacheByUsername[username]
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Client) makeRequest(parameters string) error {
|
||||
request, err := http.NewRequest("GET", "https://api.twitch.tv/helix/users"+parameters, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Header.Set("Client-ID", c.clientID)
|
||||
response, err := c.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.StatusCode >= 400 {
|
||||
return fmt.Errorf("%d GET https://api.twitch.tv/helix/users%s", response.StatusCode, parameters)
|
||||
}
|
||||
|
||||
log.Infof("%d GET https://api.twitch.tv/helix/users%s", response.StatusCode, parameters)
|
||||
|
||||
defer response.Body.Close()
|
||||
contents, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var userResp userResponse
|
||||
err = json.Unmarshal(contents, &userResp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range userResp.Data {
|
||||
userCacheByID[user.ID] = user
|
||||
userCacheByUsername[user.Login] = user
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
14
main.go
14
main.go
|
@ -28,20 +28,6 @@ type config struct {
|
|||
LogLevel string `json:"logLevel"`
|
||||
}
|
||||
|
||||
// @title justlog API
|
||||
// @version 1.0
|
||||
// @description API for twitch logs
|
||||
|
||||
// @contact.name gempir
|
||||
// @contact.url https://gempir.com
|
||||
// @contact.email gempir.dev@gmail.com
|
||||
|
||||
// @license.name MIT
|
||||
// @license.url https://github.com/gempir/justlog/blob/master/LICENSE
|
||||
|
||||
// @host logs.ivr.fi
|
||||
// @BasePath /
|
||||
|
||||
func main() {
|
||||
startTime := time.Now()
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
BIN
web/public/favicon.ico
Normal file
BIN
web/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -6,7 +6,6 @@ import reducer from "./store/reducer";
|
|||
import createInitialState from "./store/createInitialState";
|
||||
import LogSearch from './components/LogSearch';
|
||||
|
||||
|
||||
export default class App extends Component {
|
||||
|
||||
constructor(props) {
|
||||
|
|
|
@ -13,18 +13,18 @@ export default function (channel, username, year, month) {
|
|||
|
||||
dispatch(setLoading(true));
|
||||
|
||||
let channelPath = "name";
|
||||
let channelPath = "channel";
|
||||
if (channel.toLowerCase().startsWith("id:")) {
|
||||
channelPath = "id";
|
||||
}
|
||||
let usernamePath = "name";
|
||||
let usernamePath = "user";
|
||||
if (username.toLowerCase().startsWith("id:")) {
|
||||
usernamePath = "id";
|
||||
}
|
||||
|
||||
channel = channel.replace("id:", "")
|
||||
username = username.replace("id:", "")
|
||||
const url = `${getState().apiBaseUrl}/v2/${channelPath}/${channel}/${usernamePath}/${username}/${year}/${month}`;
|
||||
const url = `${getState().apiBaseUrl}/${channelPath}/${channel}/${usernamePath}/${username}/${year}/${month}`;
|
||||
|
||||
fetch(url, { headers: { "Content-Type": "application/json" } }).then((response) => {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
|
|
Loading…
Add table
Reference in a new issue