mirror of
https://github.com/Xevion/banner.git
synced 2025-12-06 13:14:25 -06:00
241 lines
5.7 KiB
Go
241 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog"
|
|
log "github.com/rs/zerolog/log"
|
|
)
|
|
|
|
// BuildRequestWithBody builds a request with the given method, path, parameters, and body
|
|
func BuildRequestWithBody(method string, path string, params map[string]string, body io.Reader) *http.Request {
|
|
// Builds a URL for the given path and parameters
|
|
requestUrl := baseURL + path
|
|
|
|
if params != nil {
|
|
takenFirst := false
|
|
for key, value := range params {
|
|
paramChar := "&"
|
|
if !takenFirst {
|
|
paramChar = "?"
|
|
takenFirst = true
|
|
}
|
|
|
|
requestUrl += paramChar + url.QueryEscape(key) + "=" + url.QueryEscape(value)
|
|
}
|
|
}
|
|
|
|
request, _ := http.NewRequest(method, requestUrl, body)
|
|
AddUserAgent(request)
|
|
return request
|
|
}
|
|
|
|
// BuildRequest builds a request with the given method, path, and parameters and an empty body
|
|
func BuildRequest(method string, path string, params map[string]string) *http.Request {
|
|
return BuildRequestWithBody(method, path, params, nil)
|
|
}
|
|
|
|
// AddUserAgent adds a false but consistent user agent to the request
|
|
func AddUserAgent(req *http.Request) {
|
|
req.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36")
|
|
}
|
|
|
|
// ContentTypeMatch checks if the response has the given content type
|
|
func ContentTypeMatch(response *http.Response, search string) bool {
|
|
// Split on commas, check if any of the types match
|
|
for _, content_type := range strings.Split(response.Header.Get("Content-Type"), ";") {
|
|
if content_type == search {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
// RandomString returns a random string of length n using the letterBytes constant
|
|
// The constant used is specifically chosen to mimic Ellucian's banner session ID generation.
|
|
func RandomString(n int) string {
|
|
b := make([]byte, n)
|
|
for i := range b {
|
|
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
// DiscordGoLogger is a specialized helper function that implements discordgo's global logging interface.
|
|
// It directs all logs to the zerolog implementation.
|
|
func DiscordGoLogger(msgL, caller int, format string, a ...interface{}) {
|
|
pc, file, line, _ := runtime.Caller(caller)
|
|
|
|
files := strings.Split(file, "/")
|
|
file = files[len(files)-1]
|
|
|
|
name := runtime.FuncForPC(pc).Name()
|
|
fns := strings.Split(name, ".")
|
|
name = fns[len(fns)-1]
|
|
|
|
msg := fmt.Sprintf(format, a...)
|
|
|
|
var event *zerolog.Event
|
|
switch msgL {
|
|
case 0:
|
|
event = log.Debug()
|
|
case 1:
|
|
event = log.Info()
|
|
case 2:
|
|
event = log.Warn()
|
|
case 3:
|
|
event = log.Error()
|
|
default:
|
|
event = log.Info()
|
|
}
|
|
|
|
event.Str("file", file).Int("line", line).Str("function", name).Msg(msg)
|
|
}
|
|
|
|
// Nonce returns a string made up of the current time in milliseconds, Unix epoch/UTC
|
|
// This is typically used as a query parameter to prevent request caching in the browser.
|
|
func Nonce() string {
|
|
return strconv.Itoa(int(time.Now().UnixMilli()))
|
|
}
|
|
|
|
// DoRequest performs & logs the request, logging and returning the response
|
|
func DoRequest(req *http.Request) (*http.Response, error) {
|
|
log.Debug().Str("method", strings.TrimRight(req.Method, " ")).Str("url", req.URL.String()).Str("query", req.URL.RawQuery).Str("content-type", req.Header.Get("Content-Type")).Msg("Request")
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
log.Err(err).Str("method", req.Method).Msg("Request Failed")
|
|
} else {
|
|
// Get the content length
|
|
contentLength := res.ContentLength
|
|
if contentLength == -1 {
|
|
contentLength, _ = strconv.ParseInt(res.Header.Get("Content-Length"), 10, 64)
|
|
}
|
|
|
|
log.Debug().Int("status", res.StatusCode).Int64("content-length", contentLength).Strs("content-type", res.Header["Content-Type"]).Msg("Response")
|
|
}
|
|
return res, err
|
|
}
|
|
|
|
func Plural(n int) string {
|
|
if n == 1 {
|
|
return ""
|
|
}
|
|
return "s"
|
|
}
|
|
|
|
func WeekdaysToString(days map[time.Weekday]bool) string {
|
|
// If no days are present
|
|
numDays := len(days)
|
|
if numDays == 0 {
|
|
return "None"
|
|
}
|
|
|
|
// If all days are present
|
|
if numDays == 7 {
|
|
return "Everyday"
|
|
}
|
|
|
|
str := ""
|
|
|
|
if days[time.Monday] {
|
|
str += "M"
|
|
}
|
|
|
|
if days[time.Tuesday] {
|
|
str += "Tu"
|
|
}
|
|
|
|
if days[time.Wednesday] {
|
|
str += "W"
|
|
}
|
|
|
|
if days[time.Thursday] {
|
|
str += "Th"
|
|
}
|
|
|
|
if days[time.Friday] {
|
|
str += "F"
|
|
}
|
|
|
|
if days[time.Saturday] {
|
|
str += "Sa"
|
|
}
|
|
|
|
if days[time.Sunday] {
|
|
str += "Su"
|
|
}
|
|
|
|
return str
|
|
}
|
|
|
|
type NaiveTime struct {
|
|
Hours uint
|
|
Minutes uint
|
|
}
|
|
|
|
func (nt NaiveTime) Sub(other NaiveTime) time.Duration {
|
|
return time.Hour*time.Duration(nt.Hours-other.Hours) + time.Minute*time.Duration(nt.Minutes-other.Minutes)
|
|
}
|
|
|
|
func ParseNaiveTime(integer uint64) NaiveTime {
|
|
minutes := uint(integer % 100)
|
|
hours := uint(integer / 100)
|
|
|
|
return NaiveTime{Hours: hours, Minutes: minutes}
|
|
}
|
|
|
|
func (nt NaiveTime) String() string {
|
|
meridiem := "AM"
|
|
hour := nt.Hours
|
|
if nt.Hours >= 12 {
|
|
meridiem = "PM"
|
|
hour -= 12
|
|
}
|
|
return fmt.Sprintf("%d:%02d%s", hour, nt.Minutes, meridiem)
|
|
}
|
|
|
|
func GetFirstEnv(key ...string) string {
|
|
for _, k := range key {
|
|
if v := os.Getenv(k); v != "" {
|
|
return v
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetPointer returns a pointer to the given value.
|
|
// This function is useful for discordgo, which inexplicably requires pointers to integers for minLength arguments.
|
|
func GetPointer(value int) *int {
|
|
return &value
|
|
}
|
|
|
|
// DumpHtml dumps a response body to a file for debugging purposes
|
|
func DumpHtml(res *http.Response) {
|
|
// Use current time as filename + /dumps/ prefix
|
|
filename := fmt.Sprintf("dumps/%d.html", time.Now().Unix())
|
|
file, err := os.Create(filename)
|
|
|
|
if err != nil {
|
|
log.Err(err).Msg("Error creating file")
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
_, err = io.Copy(file, res.Body)
|
|
if err != nil {
|
|
log.Err(err).Msg("Error copying response body")
|
|
return
|
|
}
|
|
}
|