make dep prune things
This commit is contained in:
parent
71e9986283
commit
e3a389dde4
9
mage.go
9
mage.go
|
@ -108,6 +108,15 @@ func buildBins(goos, goarch string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dep reruns `dep`.
|
||||||
|
func Dep() {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
shouldWork(ctx, nil, wd, "dep", "ensure")
|
||||||
|
shouldWork(ctx, nil, wd, "dep", "prune")
|
||||||
|
}
|
||||||
|
|
||||||
// Docker builds docker images
|
// Docker builds docker images
|
||||||
func Docker() {
|
func Docker() {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
_ "github.com/Xe/gopreload"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
pkgName = flag.String("pkg", "", "package to underscore import")
|
|
||||||
destPkgName = flag.String("dest", "", "destination package to generate")
|
|
||||||
)
|
|
||||||
|
|
||||||
const codeTemplate = `//+build go1.8
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import _ "$PACKAGE_PATH"`
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *pkgName == "" || *destPkgName == "" {
|
|
||||||
log.Fatal("must set -pkg and -dest")
|
|
||||||
}
|
|
||||||
|
|
||||||
srcDir := filepath.Join(os.Getenv("GOPATH"), "src", *destPkgName)
|
|
||||||
|
|
||||||
err := os.MkdirAll(srcDir, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fout, err := os.Create(srcDir + "/main.go")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer fout.Close()
|
|
||||||
|
|
||||||
codeBody := os.Expand(codeTemplate, func(s string) string {
|
|
||||||
if s == "PACKAGE_PATH" {
|
|
||||||
return *pkgName
|
|
||||||
}
|
|
||||||
|
|
||||||
return "no idea man"
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Fprintln(fout, codeBody)
|
|
||||||
|
|
||||||
fmt.Println("To build this plugin: ")
|
|
||||||
fmt.Println(" $ go build -buildmode plugin -o /path/to/output.so " + *destPkgName)
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
//+build go1.8
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import _ "github.com/go-sql-driver/mysql"
|
|
|
@ -1,5 +0,0 @@
|
||||||
//+build go1.8
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import _ "github.com/lib/pq"
|
|
|
@ -1,5 +0,0 @@
|
||||||
//+build go1.8
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import _ "github.com/mattn/go-sqlite3"
|
|
|
@ -1,56 +0,0 @@
|
||||||
# manhole
|
|
||||||
|
|
||||||
An opinionated HTTP manhole into Go processes.
|
|
||||||
|
|
||||||
## Assumptions This Package Makes
|
|
||||||
|
|
||||||
- Make each server instance have a unique HTTP port that is randomized by default.
|
|
||||||
This makes it very hard to accidentally route this manhole to the outside world.
|
|
||||||
If more assurance is required I personally suggest using [yubikey totp][yktotp],
|
|
||||||
but do research.
|
|
||||||
- Application code does not touch [`http.DefaultServeMux`][default-servemux]'. This is so that
|
|
||||||
administative control rods can be dynamically flipped in the case they are
|
|
||||||
needed.
|
|
||||||
- [pprof][pprof] endpoints added to `http.DefaultServeMux`. This allows easy
|
|
||||||
access to [pprof runtime tracing][pprof-tracing] to debug issues on long-running
|
|
||||||
applications like HTTP services.
|
|
||||||
- Make the manhole slightly inconvenient to put into place in production. This
|
|
||||||
helps make sure that this tool remains a debugging tool and not a part of a
|
|
||||||
long-term production rollout.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Compile this as a plugin:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ go get -d github.com/Xe/gopreload/manhole
|
|
||||||
$ go build -buildmode plugin -o manhole.so github.com/Xe/gopreload/manhole
|
|
||||||
```
|
|
||||||
|
|
||||||
Then add [`gopreload`][gopreload] to your application:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// gopreload.go
|
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is separate to make it very easy to both add into an application, but
|
|
||||||
also very easy to remove.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import _ "github.com/Xe/gopreload"
|
|
||||||
```
|
|
||||||
|
|
||||||
And at runtime add the `manhole.so` file you created earlier to the target system
|
|
||||||
somehow and add the following environment variable to its run configuration:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
GO_PRELOAD=/path/to/manhole.so
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
[pprof]: https://godoc.org/net/http/pprof
|
|
||||||
[default-servemux]: https://godoc.org/net/http#pkg-variables
|
|
||||||
[yktotp]: https://github.com/GeertJohan/yubigo
|
|
||||||
[gopreload]: https://github.com/Xe/gopreload
|
|
|
@ -1,22 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
_ "net/http/pprof"
|
|
||||||
"net/rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
l, err := net.Listen("tcp", "127.0.0.2:0")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("manhole: cannot bind to 127.0.0.2:0: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("manhole: Now listening on http://%s", l.Addr())
|
|
||||||
|
|
||||||
rpc.HandleHTTP()
|
|
||||||
go http.Serve(l, nil)
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
_ "github.com/Xe/gopreload"
|
|
||||||
"github.com/Xe/ln"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
spew()
|
|
||||||
|
|
||||||
ln.Log(ln.F{"action": "gc_spew", "who": r.RemoteAddr})
|
|
||||||
|
|
||||||
fmt.Fprintln(w, "done")
|
|
||||||
})
|
|
||||||
|
|
||||||
http.ListenAndServe(":9184", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeBuffer() []byte {
|
|
||||||
return make([]byte, rand.Intn(5000000)+5000000)
|
|
||||||
}
|
|
||||||
|
|
||||||
func spew() {
|
|
||||||
pool := make([][]byte, 20)
|
|
||||||
|
|
||||||
var m runtime.MemStats
|
|
||||||
makes := 0
|
|
||||||
for _ = range make([]struct{}, 50) {
|
|
||||||
b := makeBuffer()
|
|
||||||
makes += 1
|
|
||||||
i := rand.Intn(len(pool))
|
|
||||||
pool[i] = b
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * 250)
|
|
||||||
|
|
||||||
bytes := 0
|
|
||||||
|
|
||||||
for i := 0; i < len(pool); i++ {
|
|
||||||
if pool[i] != nil {
|
|
||||||
bytes += len(pool[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.ReadMemStats(&m)
|
|
||||||
fmt.Printf("%d,%d,%d,%d,%d,%d\n", m.HeapSys, bytes, m.HeapAlloc,
|
|
||||||
m.HeapIdle, m.HeapReleased, makes)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Xe/ln"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "started_up",
|
|
||||||
"every": "20_seconds",
|
|
||||||
"what": "gc_metrics",
|
|
||||||
})
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
time.Sleep(20 * time.Second)
|
|
||||||
gatherMetrics()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func gatherMetrics() {
|
|
||||||
stats := &runtime.MemStats{}
|
|
||||||
runtime.ReadMemStats(stats)
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"gc-collections": stats.NumGC,
|
|
||||||
"gc-stw-pause-total": stats.PauseTotalNs,
|
|
||||||
"live-object-count": stats.Mallocs - stats.Frees,
|
|
||||||
"heap-bytes": stats.Alloc,
|
|
||||||
"stack-bytes": stats.StackInuse,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"flag"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Xe/ln"
|
|
||||||
"github.com/Xe/ln/ex"
|
|
||||||
"github.com/facebookgo/flagenv"
|
|
||||||
"golang.org/x/net/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
port = flag.String("port", "2145", "http port to listen on")
|
|
||||||
tracingFamily = flag.String("trace-family", "ln example", "tracing family to use for x/net/trace")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flagenv.Parse()
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
ln.DefaultLogger.Filters = append(ln.DefaultLogger.Filters, ex.NewGoTraceLogger())
|
|
||||||
|
|
||||||
http.HandleFunc("/", handleIndex)
|
|
||||||
http.ListenAndServe(":"+*port, middlewareSpan(ex.HTTPLog(http.DefaultServeMux)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func middlewareSpan(next http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
sp := trace.New(*tracingFamily, "HTTP request")
|
|
||||||
defer sp.Finish()
|
|
||||||
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
ctx = trace.NewContext(ctx, sp)
|
|
||||||
|
|
||||||
next.ServeHTTP(w, r.WithContext(ctx))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleIndex(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
ln.Log(ctx, ln.Action("index"), ln.F{"there_is": "no_danger"})
|
|
||||||
|
|
||||||
http.Error(w, "There is no danger citizen", http.StatusOK)
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
# farbfeld filters
|
|
||||||
|
|
||||||
Filters and tools for http://tools.suckless.org/farbfeld/
|
|
|
@ -1,43 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"image"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/fogleman/primitive/primitive"
|
|
||||||
farbfeld "github.com/hullerob/go.farbfeld"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
shapeCount = flag.Int("count", 150, "number of shapes used")
|
|
||||||
repeatShapeCount = flag.Int("repeat-count", 0, "number of extra shapes drawn in each step")
|
|
||||||
alpha = flag.Int("alpha", 128, "alpha of all shapes")
|
|
||||||
)
|
|
||||||
|
|
||||||
func stepImg(img image.Image, count int) image.Image {
|
|
||||||
bg := primitive.MakeColor(primitive.AverageImageColor(img))
|
|
||||||
model := primitive.NewModel(img, bg, 512, runtime.NumCPU())
|
|
||||||
|
|
||||||
for range make([]struct{}, count) {
|
|
||||||
model.Step(primitive.ShapeTypeTriangle, *alpha, *repeatShapeCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
return model.Context.Image()
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
img, err := farbfeld.Decode(os.Stdin)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = farbfeld.Encode(os.Stdout, stepImg(img, *shapeCount))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
b572f0728b691aae4256edb2e408279146eafe52 github.com/hullerob/go.farbfeld
|
|
||||||
ee8994ff90057955c428a5a949da5d064bf3ce6b github.com/fogleman/gg
|
|
||||||
80f39ceaa8f4c66acb28aba6abe6b15128c06113 github.com/fogleman/primitive/primitive
|
|
||||||
bcfeb16b74e8aea9e2fe043406f2ef74b1cb0759 github.com/golang/freetype/raster
|
|
||||||
bcfeb16b74e8aea9e2fe043406f2ef74b1cb0759 github.com/golang/freetype/truetype
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/draw
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/font
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/font/basicfont
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/math/f64
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/math/fixed
|
|
|
@ -1 +0,0 @@
|
||||||
amerge
|
|
|
@ -1,4 +0,0 @@
|
||||||
amerge
|
|
||||||
======
|
|
||||||
|
|
||||||
Utility for scraping and (later) merging atheme databases.
|
|
|
@ -1,591 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
NoSuchAcctErr = errors.New("There is no such account by that name")
|
|
||||||
NoSuchChanErr = errors.New("There is no such channel by that name")
|
|
||||||
NoSuchGroupErr = errors.New("There is no such group by that name")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Database struct {
|
|
||||||
Version string
|
|
||||||
ModuleDeps []*Line
|
|
||||||
LastUID string
|
|
||||||
|
|
||||||
LastKID int
|
|
||||||
LastXID int
|
|
||||||
LastQID int
|
|
||||||
|
|
||||||
Accounts map[string]*Account
|
|
||||||
Channels map[string]*Channel
|
|
||||||
Bots map[string]*Bot
|
|
||||||
Groups map[string]*Group
|
|
||||||
Names map[string]*Name
|
|
||||||
Badwords []Badword
|
|
||||||
Klines []Kline
|
|
||||||
ConnectInfos []ConnectInfo
|
|
||||||
HostOffers []HostOffer
|
|
||||||
HostRequests []HostRequest
|
|
||||||
|
|
||||||
ClonesExemptions []ClonesExemption
|
|
||||||
Rwatches []Line
|
|
||||||
|
|
||||||
lines []*Line
|
|
||||||
file *bufio.Scanner
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDatabase(fname string) (db *Database, err error) {
|
|
||||||
fin, err := os.Open(fname)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
db = &Database{
|
|
||||||
Accounts: make(map[string]*Account),
|
|
||||||
Channels: make(map[string]*Channel),
|
|
||||||
Bots: make(map[string]*Bot),
|
|
||||||
Groups: make(map[string]*Group),
|
|
||||||
Names: make(map[string]*Name),
|
|
||||||
}
|
|
||||||
|
|
||||||
db.file = bufio.NewScanner(fin)
|
|
||||||
|
|
||||||
for db.file.Scan() {
|
|
||||||
rawline := db.file.Text()
|
|
||||||
|
|
||||||
l := &Line{}
|
|
||||||
split := strings.Split(rawline, " ")
|
|
||||||
|
|
||||||
l.Verb = split[0]
|
|
||||||
l.Args = split[1:]
|
|
||||||
|
|
||||||
db.lines = append(db.lines, l)
|
|
||||||
|
|
||||||
switch l.Verb {
|
|
||||||
case "DBV": // Database version
|
|
||||||
db.Version = l.Args[0]
|
|
||||||
|
|
||||||
case "MDEP": // Module dependency
|
|
||||||
db.ModuleDeps = append(db.ModuleDeps, l)
|
|
||||||
|
|
||||||
case "LUID": // Last used UID for accounts
|
|
||||||
db.LastUID = l.Args[0]
|
|
||||||
|
|
||||||
case "MU": // Create a user account
|
|
||||||
a := &Account{
|
|
||||||
Name: l.Args[1],
|
|
||||||
UID: l.Args[0],
|
|
||||||
Email: l.Args[3],
|
|
||||||
Password: l.Args[2],
|
|
||||||
Regtime: l.Args[4],
|
|
||||||
LastSeenTime: l.Args[5],
|
|
||||||
|
|
||||||
Metadata: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Accounts[strings.ToUpper(a.Name)] = a
|
|
||||||
|
|
||||||
case "MDU": // User metadata
|
|
||||||
account, err := db.GetAccount(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
account.Metadata[l.Args[1]] = strings.Join(l.Args[2:], " ")
|
|
||||||
|
|
||||||
case "AC": // Account access rule (prevents nickserv protections for a mask)
|
|
||||||
account, err := db.GetAccount(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ac := Access{
|
|
||||||
AccountName: l.Args[0],
|
|
||||||
Mask: l.Args[1],
|
|
||||||
}
|
|
||||||
|
|
||||||
account.AccessList = append(account.AccessList, ac)
|
|
||||||
|
|
||||||
case "MI": // MemoServ IGNORE for a user
|
|
||||||
account, err := db.GetAccount(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
account.Ignores = append(account.Ignores, l.Args[1])
|
|
||||||
|
|
||||||
case "MN": // Account nickname in nick group
|
|
||||||
account, err := db.GetAccount(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gn := GroupedNick{
|
|
||||||
Account: l.Args[0],
|
|
||||||
Name: l.Args[1],
|
|
||||||
Regtime: l.Args[2],
|
|
||||||
Seentime: l.Args[3],
|
|
||||||
}
|
|
||||||
|
|
||||||
account.Nicks = append(account.Nicks, gn)
|
|
||||||
|
|
||||||
case "MCFP": // Certificate Fingerprint
|
|
||||||
account, err := db.GetAccount(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
account.CertFP = append(account.CertFP, l.Args[1])
|
|
||||||
|
|
||||||
case "ME": // Memo in user's inbox
|
|
||||||
account, err := db.GetAccount(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m := Memo{
|
|
||||||
Inbox: l.Args[0],
|
|
||||||
From: l.Args[1],
|
|
||||||
Time: l.Args[2],
|
|
||||||
Read: l.Args[3] == "1",
|
|
||||||
Contents: strings.Join(l.Args[4:], " "),
|
|
||||||
}
|
|
||||||
|
|
||||||
account.Memos = append(account.Memos, m)
|
|
||||||
|
|
||||||
case "MC": // Create a channel
|
|
||||||
mlockon, err := strconv.ParseInt(l.Args[4], 16, 0)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &Channel{
|
|
||||||
Name: l.Args[0],
|
|
||||||
Regtime: l.Args[1],
|
|
||||||
Seentime: l.Args[2],
|
|
||||||
Flags: l.Args[3],
|
|
||||||
MlockOn: int(mlockon),
|
|
||||||
|
|
||||||
Metadata: make(map[string]string),
|
|
||||||
AccessMetadata: make(map[string]AccessMetadata),
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Channels[strings.ToUpper(l.Args[0])] = c
|
|
||||||
|
|
||||||
case "CA": // ChanAcs
|
|
||||||
c, err := db.GetChannel(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read channel %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ca := ChanAc{
|
|
||||||
Channel: l.Args[0],
|
|
||||||
Account: l.Args[1],
|
|
||||||
FlagSet: l.Args[2],
|
|
||||||
DateGranted: l.Args[3],
|
|
||||||
WhoGranted: l.Args[4],
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Access = append(c.Access, ca)
|
|
||||||
|
|
||||||
case "MDC": // Channel metadata
|
|
||||||
c, err := db.GetChannel(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read channel %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Metadata[l.Args[1]] = strings.Join(l.Args[2:], " ")
|
|
||||||
|
|
||||||
case "MDA": // Channel-based entity key->value
|
|
||||||
c, err := db.GetChannel(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read channel %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
amd := AccessMetadata{
|
|
||||||
ChannelName: l.Args[0],
|
|
||||||
Entity: l.Args[1],
|
|
||||||
Key: l.Args[2],
|
|
||||||
Value: l.Args[3],
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AccessMetadata[strings.ToUpper(amd.Key)] = amd
|
|
||||||
|
|
||||||
case "NAM":
|
|
||||||
nam := &Name{
|
|
||||||
Name: l.Args[0],
|
|
||||||
|
|
||||||
Metadata: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Names[strings.ToUpper(nam.Name)] = nam
|
|
||||||
|
|
||||||
case "MDN":
|
|
||||||
nam, ok := db.Names[strings.ToUpper(l.Args[0])]
|
|
||||||
if !ok {
|
|
||||||
panic("Atheme is broken with things")
|
|
||||||
}
|
|
||||||
|
|
||||||
nam.Metadata[l.Args[1]] = strings.Join(l.Args[2:], " ")
|
|
||||||
|
|
||||||
case "KID": // Biggest kline id used
|
|
||||||
kid, err := strconv.ParseInt(l.Args[0], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
panic("atheme is broken with KID " + l.Args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
db.LastKID = int(kid)
|
|
||||||
|
|
||||||
case "XID": // Biggest xline id used
|
|
||||||
xid, err := strconv.ParseInt(l.Args[0], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
panic("atheme is broken with XID " + l.Args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
db.LastXID = int(xid)
|
|
||||||
|
|
||||||
case "QID": // Biggest qline id used
|
|
||||||
qid, err := strconv.ParseInt(l.Args[0], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
panic("atheme is broken with QID " + l.Args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
db.LastQID = int(qid)
|
|
||||||
|
|
||||||
case "KL": // kline
|
|
||||||
id, err := strconv.ParseInt(l.Args[0], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
kl := Kline{
|
|
||||||
ID: int(id),
|
|
||||||
User: l.Args[1],
|
|
||||||
Host: l.Args[2],
|
|
||||||
Duration: l.Args[3],
|
|
||||||
DateSet: l.Args[4],
|
|
||||||
WhoSet: l.Args[5],
|
|
||||||
Reason: strings.Join(l.Args[6:], " "),
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Klines = append(db.Klines, kl)
|
|
||||||
|
|
||||||
case "BOT": // BotServ bot
|
|
||||||
bot := &Bot{
|
|
||||||
Nick: l.Args[0],
|
|
||||||
User: l.Args[1],
|
|
||||||
Host: l.Args[2],
|
|
||||||
IsPrivate: l.Args[3] == "1",
|
|
||||||
CreationDate: l.Args[4],
|
|
||||||
Gecos: l.Args[5],
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Bots[strings.ToUpper(bot.Nick)] = bot
|
|
||||||
|
|
||||||
case "BW": // BADWORDS entry
|
|
||||||
bw := Badword{
|
|
||||||
Mask: l.Args[0],
|
|
||||||
TimeSet: l.Args[1],
|
|
||||||
Setter: l.Args[2],
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(l.Args) == 5 {
|
|
||||||
bw.Channel = l.Args[3]
|
|
||||||
bw.Action = l.Args[4]
|
|
||||||
} else {
|
|
||||||
bw.Setter = bw.Setter + " " + l.Args[3]
|
|
||||||
bw.Channel = l.Args[4]
|
|
||||||
bw.Action = l.Args[5]
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Badwords = append(db.Badwords, bw) // TODO: move this to Channel?
|
|
||||||
|
|
||||||
case "GRP": // Group
|
|
||||||
g := &Group{
|
|
||||||
UID: l.Args[0],
|
|
||||||
Name: l.Args[1],
|
|
||||||
CreationDate: l.Args[2],
|
|
||||||
Flags: l.Args[3],
|
|
||||||
|
|
||||||
Metadata: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Groups[strings.ToUpper(l.Args[1])] = g
|
|
||||||
|
|
||||||
case "GACL": // Group access list
|
|
||||||
g, err := db.GetGroup(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read group %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gacl := GroupACL{
|
|
||||||
GroupName: l.Args[0],
|
|
||||||
AccountName: l.Args[1],
|
|
||||||
Flags: l.Args[2],
|
|
||||||
}
|
|
||||||
|
|
||||||
g.ACL = append(g.ACL, gacl)
|
|
||||||
|
|
||||||
case "MDG": // Group Metadata
|
|
||||||
g, err := db.GetGroup(l.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Tried to read group %s but got %#v???", l.Args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Metadata[l.Args[1]] = strings.Join(l.Args[2:], " ")
|
|
||||||
|
|
||||||
case "CLONES-EX": // CLONES exemptions
|
|
||||||
ce := ClonesExemption{
|
|
||||||
IP: l.Args[0],
|
|
||||||
Min: l.Args[1],
|
|
||||||
Max: l.Args[2],
|
|
||||||
Expiry: l.Args[3],
|
|
||||||
Reason: strings.Join(l.Args[4:], " "),
|
|
||||||
}
|
|
||||||
|
|
||||||
db.ClonesExemptions = append(db.ClonesExemptions, ce)
|
|
||||||
|
|
||||||
case "LI": // InfoServ INFO posts
|
|
||||||
ci := ConnectInfo{
|
|
||||||
Creator: l.Args[0],
|
|
||||||
Topic: l.Args[1],
|
|
||||||
CreationDate: l.Args[2],
|
|
||||||
Body: strings.Join(l.Args[3:], " "),
|
|
||||||
}
|
|
||||||
|
|
||||||
db.ConnectInfos = append(db.ConnectInfos, ci)
|
|
||||||
|
|
||||||
case "HO": // Vhost offer
|
|
||||||
var ho HostOffer
|
|
||||||
|
|
||||||
if len(l.Args) == 3 {
|
|
||||||
ho = HostOffer{
|
|
||||||
Vhost: l.Args[0],
|
|
||||||
CreationDate: l.Args[1],
|
|
||||||
Creator: l.Args[2],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ho = HostOffer{
|
|
||||||
Group: l.Args[0],
|
|
||||||
Vhost: l.Args[1],
|
|
||||||
CreationDate: l.Args[2],
|
|
||||||
Creator: l.Args[3],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.HostOffers = append(db.HostOffers, ho)
|
|
||||||
|
|
||||||
case "HR": // Vhost request
|
|
||||||
hr := HostRequest{
|
|
||||||
Account: l.Args[0],
|
|
||||||
Vhost: l.Args[1],
|
|
||||||
RequestTime: l.Args[2],
|
|
||||||
}
|
|
||||||
|
|
||||||
db.HostRequests = append(db.HostRequests, hr)
|
|
||||||
|
|
||||||
// Verbs to ignore
|
|
||||||
case "":
|
|
||||||
|
|
||||||
default:
|
|
||||||
fmt.Printf("%#v\n", l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetAccount(name string) (*Account, error) {
|
|
||||||
account, ok := db.Accounts[strings.ToUpper(name)]
|
|
||||||
if !ok {
|
|
||||||
return nil, NoSuchAcctErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return account, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetChannel(name string) (*Channel, error) {
|
|
||||||
channel, ok := db.Channels[strings.ToUpper(name)]
|
|
||||||
if !ok {
|
|
||||||
return nil, NoSuchChanErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return channel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetGroup(name string) (*Group, error) {
|
|
||||||
group, ok := db.Groups[strings.ToUpper(name)]
|
|
||||||
if !ok {
|
|
||||||
return nil, NoSuchGroupErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return group, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetBot(name string) (*Bot, error) {
|
|
||||||
group, ok := db.Bots[strings.ToUpper(name)]
|
|
||||||
if !ok {
|
|
||||||
return nil, NoSuchGroupErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return group, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Line struct {
|
|
||||||
Verb string
|
|
||||||
Args []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Account struct {
|
|
||||||
Name string
|
|
||||||
Email string
|
|
||||||
Flags string
|
|
||||||
Kind string
|
|
||||||
UID string
|
|
||||||
Password string
|
|
||||||
|
|
||||||
Regtime string
|
|
||||||
LastSeenTime string
|
|
||||||
|
|
||||||
Metadata map[string]string
|
|
||||||
Nicks []GroupedNick
|
|
||||||
Memos []Memo
|
|
||||||
CertFP []string
|
|
||||||
AccessList []Access
|
|
||||||
Ignores []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Access struct {
|
|
||||||
AccountName string
|
|
||||||
Mask string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Name struct {
|
|
||||||
Name string
|
|
||||||
Metadata map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type GroupedNick struct {
|
|
||||||
Account string
|
|
||||||
Name string
|
|
||||||
Regtime string
|
|
||||||
Seentime string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Memo struct {
|
|
||||||
Inbox string
|
|
||||||
From string // an account name
|
|
||||||
Time string
|
|
||||||
Read bool
|
|
||||||
Contents string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Channel struct {
|
|
||||||
Name string
|
|
||||||
Regtime string
|
|
||||||
Seentime string
|
|
||||||
Flags string
|
|
||||||
MlockOn int
|
|
||||||
MlockOff int
|
|
||||||
MlockLimit int
|
|
||||||
MlockKey string
|
|
||||||
|
|
||||||
Access []ChanAc
|
|
||||||
Metadata map[string]string
|
|
||||||
AccessMetadata map[string]AccessMetadata
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccessMetadata struct {
|
|
||||||
ChannelName string
|
|
||||||
Entity string
|
|
||||||
Key string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChanAc struct {
|
|
||||||
Channel string
|
|
||||||
Account string
|
|
||||||
FlagSet string
|
|
||||||
DateGranted string
|
|
||||||
WhoGranted string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Kline struct {
|
|
||||||
ID int
|
|
||||||
User string
|
|
||||||
Host string
|
|
||||||
Duration string
|
|
||||||
DateSet string
|
|
||||||
WhoSet string
|
|
||||||
Reason string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bot struct {
|
|
||||||
Nick string
|
|
||||||
User string
|
|
||||||
Host string
|
|
||||||
IsPrivate bool
|
|
||||||
CreationDate string
|
|
||||||
Gecos string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Badword struct {
|
|
||||||
Mask string
|
|
||||||
TimeSet string
|
|
||||||
Setter string // can be Foo or Foo (Bar)
|
|
||||||
Channel string
|
|
||||||
Action string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Group struct {
|
|
||||||
UID string
|
|
||||||
Name string
|
|
||||||
CreationDate string
|
|
||||||
Flags string
|
|
||||||
|
|
||||||
ACL []GroupACL
|
|
||||||
Metadata map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type GroupACL struct {
|
|
||||||
GroupName string
|
|
||||||
AccountName string
|
|
||||||
Flags string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConnectInfo struct {
|
|
||||||
Creator string
|
|
||||||
Topic string
|
|
||||||
CreationDate string
|
|
||||||
Body string
|
|
||||||
}
|
|
||||||
|
|
||||||
type HostOffer struct { // if args number is 3 no group
|
|
||||||
Group string
|
|
||||||
Vhost string
|
|
||||||
CreationDate string
|
|
||||||
Creator string
|
|
||||||
}
|
|
||||||
|
|
||||||
type HostRequest struct {
|
|
||||||
Account string
|
|
||||||
Vhost string
|
|
||||||
RequestTime string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClonesExemption struct {
|
|
||||||
IP string
|
|
||||||
Min string
|
|
||||||
Max string
|
|
||||||
Expiry string
|
|
||||||
Reason string
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
leftFname = flag.String("left-db", "./left.db", "database to read from to compare as the left hand")
|
|
||||||
rightFname = flag.String("right-db", "./right.db", "\" for the right hand side")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
leftDB, err := NewDatabase(*leftFname)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = leftDB
|
|
||||||
|
|
||||||
rightDB, err := NewDatabase(*rightFname)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = rightDB
|
|
||||||
|
|
||||||
result := &Database{
|
|
||||||
Accounts: make(map[string]*Account),
|
|
||||||
Channels: make(map[string]*Channel),
|
|
||||||
Bots: make(map[string]*Bot),
|
|
||||||
Groups: make(map[string]*Group),
|
|
||||||
Names: make(map[string]*Name),
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = result
|
|
||||||
|
|
||||||
// Compare accounts and grouped nicks in left database to names in right database
|
|
||||||
// this is O(scary)
|
|
||||||
|
|
||||||
for leftAccountName, acc := range leftDB.Accounts {
|
|
||||||
for _, leftGroupedNick := range acc.Nicks {
|
|
||||||
conflictAcc, err := rightDB.GetAccount(leftGroupedNick.Name)
|
|
||||||
if err != nil {
|
|
||||||
goto botcheck
|
|
||||||
}
|
|
||||||
|
|
||||||
if conflictAcc.Email == acc.Email {
|
|
||||||
//log.Printf("Can ignore %s, they are the same user by email account", acc.Name)
|
|
||||||
goto botcheck
|
|
||||||
}
|
|
||||||
log.Printf(
|
|
||||||
"While trying to see if %s:%s is present in right database, found a conflict with %s",
|
|
||||||
acc.Name, leftGroupedNick.Name, conflictAcc.Name,
|
|
||||||
)
|
|
||||||
log.Printf(
|
|
||||||
"left: %s %s %s %s",
|
|
||||||
acc.Name, acc.Email, acc.Regtime, acc.LastSeenTime,
|
|
||||||
)
|
|
||||||
log.Printf(
|
|
||||||
"right: %s %s %s %s",
|
|
||||||
conflictAcc.Name, conflictAcc.Email, conflictAcc.Regtime, conflictAcc.LastSeenTime,
|
|
||||||
)
|
|
||||||
|
|
||||||
botcheck:
|
|
||||||
//log.Printf("Checking for bot collisions for %s:%s...", acc.Name, leftGroupedNick.Name)
|
|
||||||
conflictBot, err := rightDB.GetBot(leftGroupedNick.Name)
|
|
||||||
if err != nil {
|
|
||||||
goto next
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.ToUpper(conflictBot.Nick) == leftAccountName {
|
|
||||||
log.Printf("Nickname %s conflicts with right's bot %s", leftGroupedNick.Name, conflictBot.Nick)
|
|
||||||
}
|
|
||||||
next:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
.env
|
|
|
@ -1,13 +0,0 @@
|
||||||
from "xena/go"
|
|
||||||
|
|
||||||
workdir "/"
|
|
||||||
copy "main.go", "/go/src/github.com/Xe/tools/irc/bncadmin/main.go"
|
|
||||||
copy "vendor", "/go/src/github.com/Xe/tools/irc/bncadmin/"
|
|
||||||
run "go install github.com/Xe/tools/irc/bncadmin && cp /go/bin/bncadmin /usr/bin/bncadmin"
|
|
||||||
|
|
||||||
run "rm -rf /usr/local/go /go && apk del bash gcc musl-dev openssl go"
|
|
||||||
flatten
|
|
||||||
|
|
||||||
cmd "/usr/bin/bncadmin"
|
|
||||||
|
|
||||||
tag "xena/bncadmin"
|
|
|
@ -1,191 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/belak/irc"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
bncUsername = needEnv("BNC_USERNAME")
|
|
||||||
bncPassword = needEnv("BNC_PASSWORD")
|
|
||||||
bncServer = needEnv("BNC_SERVER")
|
|
||||||
serverSuffixExpected = needEnv("SERVER_SUFFIX")
|
|
||||||
)
|
|
||||||
|
|
||||||
func needEnv(key string) string {
|
|
||||||
v := os.Getenv(key)
|
|
||||||
if v == "" {
|
|
||||||
log.Fatal("need value for " + key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.Println("Bot connecting to " + bncServer)
|
|
||||||
conn, err := tls.Dial("tcp", bncServer, &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
c := irc.NewClient(conn, irc.ClientConfig{
|
|
||||||
Nick: "admin",
|
|
||||||
Pass: fmt.Sprintf("%s:%s", bncUsername, bncPassword),
|
|
||||||
User: "BNCbot",
|
|
||||||
Name: "BNC admin bot",
|
|
||||||
|
|
||||||
Handler: NewBot(),
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, cap := range []string{"userhost-in-names", "multi-prefix", "znc.in/server-time-iso"} {
|
|
||||||
c.Writef("CAP REQ %s", cap)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.Run()
|
|
||||||
if err != nil {
|
|
||||||
main()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bot struct {
|
|
||||||
setupDaemon sync.Once
|
|
||||||
lookingForUserNetworks bool
|
|
||||||
|
|
||||||
// i am sorry
|
|
||||||
launUsername string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBot() *Bot {
|
|
||||||
return &Bot{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) Handle(c *irc.Client, m *irc.Message) {
|
|
||||||
b.setupDaemon.Do(func() {
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
b.lookingForUserNetworks = true
|
|
||||||
c.Writef("PRIVMSG *status ListAllUserNetworks")
|
|
||||||
time.Sleep(2 * time.Second) // always sleep 2
|
|
||||||
b.lookingForUserNetworks = false
|
|
||||||
|
|
||||||
time.Sleep(1 * time.Hour)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
// log.Printf("in >> %s", m)
|
|
||||||
|
|
||||||
switch m.Command {
|
|
||||||
case "PRIVMSG":
|
|
||||||
if m.Prefix.Name == "*status" {
|
|
||||||
b.HandleStarStatus(c, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(m.Prefix.Name, "?") {
|
|
||||||
b.HandlePartyLineCommand(c, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.Params[0] == "#bnc" {
|
|
||||||
b.HandleCommand(c, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "NOTICE":
|
|
||||||
if m.Prefix.Name == "*status" {
|
|
||||||
f := strings.Fields(m.Trailing())
|
|
||||||
if f[0] == "***" {
|
|
||||||
log.Println(m.Trailing())
|
|
||||||
// look up geoip and log here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) HandleStarStatus(c *irc.Client, m *irc.Message) {
|
|
||||||
if b.lookingForUserNetworks {
|
|
||||||
if strings.HasPrefix(m.Trailing(), "| ") {
|
|
||||||
f := strings.Fields(m.Trailing())
|
|
||||||
|
|
||||||
switch len(f) {
|
|
||||||
case 11: // user name line
|
|
||||||
// 11: []string{"|", "AzureDiamond", "|", "N/A", "|", "0", "|", "|", "|", "|", "|"}
|
|
||||||
username := f[1]
|
|
||||||
b.launUsername = username
|
|
||||||
|
|
||||||
case 15: // server and nick!user@host line
|
|
||||||
// 15: []string{"|", "`-", "|", "PonyChat", "|", "0", "|", "Yes", "|", "amethyststar.ponychat.net", "|", "test!test@lypbmzxixk.ponychat.net", "|", "1", "|"}
|
|
||||||
server := f[9]
|
|
||||||
network := f[3]
|
|
||||||
if !strings.HasSuffix(server, serverSuffixExpected) {
|
|
||||||
log.Printf("%s is using the BNC to connect to unknown server %s, removing permissions", b.launUsername, server)
|
|
||||||
b.RemoveNetwork(c, b.launUsername, network)
|
|
||||||
c.Writef("PRIVMSG ?%s :You have violated the terms of the BNC service and your account has been disabled. Please contact PonyChat staff to appeal this.", b.launUsername)
|
|
||||||
c.Writef("PRIVMSG *blockuser block %s", b.launUsername)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) HandlePartyLineCommand(c *irc.Client, m *irc.Message) {
|
|
||||||
split := strings.Fields(m.Trailing())
|
|
||||||
username := m.Prefix.Name[1:]
|
|
||||||
|
|
||||||
if len(split) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch strings.ToLower(split[0]) {
|
|
||||||
case "help":
|
|
||||||
c.Writef("PRIVMSG ?%s :Commands available:", username)
|
|
||||||
c.Writef("PRIVMSG ?%s :- ChangeName <new desired \"real name\">", username)
|
|
||||||
c.Writef("PRIVMSG ?%s : Changes your IRC \"real name\" to a new value instead of the default", username)
|
|
||||||
c.Writef("PRIVMSG ?%s :- Reconnect", username)
|
|
||||||
c.Writef("PRIVMSG ?%s : Disconnects from PonyChat and connects to PonyChat again", username)
|
|
||||||
c.Writef("PRIVMSG ?%s :- Help", username)
|
|
||||||
c.Writef("PRIVMSG ?%s : Shows this Message", username)
|
|
||||||
case "changename":
|
|
||||||
if len(split) < 1 {
|
|
||||||
c.Writef("NOTICE %s :Usage: ChangeName <new desired \"real name\">")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
gecos := strings.Join(split[1:], " ")
|
|
||||||
c.Writef("PRIVMSG *controlpanel :Set RealName %s %s", username, gecos)
|
|
||||||
c.Writef("PRIVMSG ?%s :Please reply %q to confirm changing your \"real name\" to: %s", username, "Reconnect", gecos)
|
|
||||||
case "reconnect":
|
|
||||||
c.Writef("PRIVMSG ?%s :Reconnecting...", username)
|
|
||||||
c.Writef("PRIVMSG *controlpanel Reconnect %s PonyChat", username)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) HandleCommand(c *irc.Client, m *irc.Message) {
|
|
||||||
split := strings.Fields(m.Trailing())
|
|
||||||
if split[0][0] == ';' {
|
|
||||||
switch strings.ToLower(split[0][1:]) {
|
|
||||||
case "request":
|
|
||||||
c.Write("PRIVMSG #bnc :In order to request a BNC account, please connect to the bouncer server (bnc.ponychat.net, ssl port 6697, allow untrusted certs) with your nickserv username and passsword in the server password field (example: AzureDiamond:hunter2)")
|
|
||||||
case "help":
|
|
||||||
c.Write("PRIVMSG #bnc :PonyChat bouncer help is available here: https://ponychat.net/help/bnc/")
|
|
||||||
case "rules":
|
|
||||||
c.Write("PRIVMSG #bnc :Terms of the BNC")
|
|
||||||
c.Write("PRIVMSG #bnc :- Do not use the BNC to evade channel bans")
|
|
||||||
c.Write("PRIVMSG #bnc :- Do not use the BNC to violate any network rules")
|
|
||||||
c.Write("PRIVMSG #bnc :- Do not use the BNC to connect to any other IRC network than PonyChat")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) RemoveNetwork(c *irc.Client, username, network string) {
|
|
||||||
c.Writef("PRIVMSG *controlpanel :DelNetwork %s %s", username, network)
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
set -x
|
|
||||||
|
|
||||||
box box.rb
|
|
||||||
docker push xena/bncadmin
|
|
||||||
|
|
||||||
hyper rm -f bncadmin ||:
|
|
||||||
hyper pull xena/bncadmin
|
|
||||||
hyper run --name bncadmin --restart=always -dit --size s1 --env-file .env xena/bncadmin
|
|
|
@ -1,3 +0,0 @@
|
||||||
fd04337c94f98ab7c2ef34fbeb4e821284775095 github.com/belak/irc
|
|
||||||
4ed13390c0acd2ff4e371e64d8b97c8954138243 github.com/joho/godotenv
|
|
||||||
4ed13390c0acd2ff4e371e64d8b97c8954138243 github.com/joho/godotenv/autoload
|
|
|
@ -1 +0,0 @@
|
||||||
.env
|
|
|
@ -1,301 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Xe/ln"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
|
||||||
irc "gopkg.in/irc.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
addr = os.Getenv("SERVER")
|
|
||||||
password = os.Getenv("PASSWORD")
|
|
||||||
|
|
||||||
sclock sync.Mutex
|
|
||||||
scores map[string]float64
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
scores = map[string]float64{}
|
|
||||||
|
|
||||||
conn, err := tls.Dial("tcp", addr, &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "connected",
|
|
||||||
"where": addr,
|
|
||||||
})
|
|
||||||
|
|
||||||
cli := irc.NewClient(conn, irc.ClientConfig{
|
|
||||||
Handler: irc.HandlerFunc(scoreCleveland),
|
|
||||||
Nick: "Xena",
|
|
||||||
User: "xena",
|
|
||||||
Name: "cleveland brown termination bot",
|
|
||||||
Pass: password,
|
|
||||||
})
|
|
||||||
|
|
||||||
ff := ln.FilterFunc(func(e ln.Event) bool {
|
|
||||||
if val, ok := e.Data["svclog"]; ok && val.(bool) {
|
|
||||||
delete(e.Data, "svclog")
|
|
||||||
|
|
||||||
line, err := ln.DefaultFormatter.Format(e)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err})
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cli.Writef("PRIVMSG #services :%s", string(line))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
ln.DefaultLogger.Filters = append(ln.DefaultLogger.Filters, ff)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
time.Sleep(30 * time.Second)
|
|
||||||
|
|
||||||
sclock.Lock()
|
|
||||||
defer sclock.Unlock()
|
|
||||||
|
|
||||||
changed := 0
|
|
||||||
ignored := 0
|
|
||||||
|
|
||||||
for key, sc := range scores {
|
|
||||||
if sc >= notifyThreshold {
|
|
||||||
ignored++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
scores[key] = sc / 100
|
|
||||||
changed++
|
|
||||||
}
|
|
||||||
|
|
||||||
sclock.Unlock()
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "nerfed_scores",
|
|
||||||
"changed": changed,
|
|
||||||
"ignored": ignored,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
time.Sleep(5 * time.Minute)
|
|
||||||
|
|
||||||
sclock.Lock()
|
|
||||||
defer sclock.Unlock()
|
|
||||||
|
|
||||||
nsc := map[string]float64{}
|
|
||||||
|
|
||||||
halved := 0
|
|
||||||
rem := 0
|
|
||||||
|
|
||||||
for key, score := range scores {
|
|
||||||
if score > 0.01 {
|
|
||||||
if score > 3 {
|
|
||||||
score = score / 2
|
|
||||||
halved++
|
|
||||||
}
|
|
||||||
|
|
||||||
nsc[key] = score
|
|
||||||
} else {
|
|
||||||
rem++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scores = nsc
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "reaped_scores",
|
|
||||||
"removed": rem,
|
|
||||||
"halved": halved,
|
|
||||||
"svclog": true,
|
|
||||||
})
|
|
||||||
|
|
||||||
sclock.Unlock()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "accepting_input",
|
|
||||||
"svclog": true,
|
|
||||||
})
|
|
||||||
|
|
||||||
cli.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
notifyThreshold = 3
|
|
||||||
autobanThreshold = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
func scoreCleveland(c *irc.Client, m *irc.Message) {
|
|
||||||
if m.Trailing() == "!ohshitkillit" && m.Prefix.Host == "ponychat.net" {
|
|
||||||
ln.Fatal(ln.F{
|
|
||||||
"action": "emergency_stop",
|
|
||||||
"user": m.Prefix.String(),
|
|
||||||
"channel": m.Params[0],
|
|
||||||
"svclog": true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
sclock.Lock()
|
|
||||||
defer sclock.Unlock()
|
|
||||||
|
|
||||||
if m.Command != "PRIVMSG" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if !strings.HasSuffix(m.Params[0], monitorChan) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
switch m.Params[0] {
|
|
||||||
case "#services", "#/dev/syslog":
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch m.Prefix.Name {
|
|
||||||
case "Taz", "cadance-syslog", "FromDiscord", "Sonata_Dusk", "CQ_Discord", "Onion":
|
|
||||||
return
|
|
||||||
|
|
||||||
case "Sparkler":
|
|
||||||
// (Sparkler) lol
|
|
||||||
// (Sparkler) don't banzor me :(
|
|
||||||
return
|
|
||||||
|
|
||||||
case "Aeyris":
|
|
||||||
// known shitposter, collison risk :(
|
|
||||||
return
|
|
||||||
|
|
||||||
case "Ryunosuke", "WaterStar":
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sc, ok := scores[m.Prefix.Host]
|
|
||||||
if !ok {
|
|
||||||
sc = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, line := range lines {
|
|
||||||
if strings.Contains(strings.ToLower(m.Trailing()), line) {
|
|
||||||
sc += 1
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "siren_compare",
|
|
||||||
"channel": m.Params[0],
|
|
||||||
"user": m.Prefix.String(),
|
|
||||||
"scoredelta": 1,
|
|
||||||
"svclog": true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
thisLine := strings.ToLower(m.Trailing())
|
|
||||||
|
|
||||||
for _, efnLine := range efknockr {
|
|
||||||
if strings.Contains(thisLine, strings.ToLower(efnLine)) {
|
|
||||||
sc += 3
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "efknockr_detected",
|
|
||||||
"score": sc,
|
|
||||||
"user": m.Prefix.String(),
|
|
||||||
"channel": m.Params[0],
|
|
||||||
"delta": 3,
|
|
||||||
"svclog": true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scores[m.Prefix.Host] = sc
|
|
||||||
|
|
||||||
if sc >= notifyThreshold {
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "warn",
|
|
||||||
"channel": m.Params[0],
|
|
||||||
"user": m.Prefix.String(),
|
|
||||||
"score": sc,
|
|
||||||
"svclog": true,
|
|
||||||
"ping": "Xena",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if sc >= autobanThreshold {
|
|
||||||
c.Writef("PRIVMSG OperServ :AKILL ADD %s spamming | Cleveland show spammer", m.Prefix.Name)
|
|
||||||
c.Writef("PRIVMSG %s :Sorry for that, he's gone now.", m.Params[0])
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "kline_added",
|
|
||||||
"channel": m.Params[0],
|
|
||||||
"user": m.Prefix.String(),
|
|
||||||
"score": sc,
|
|
||||||
"svclog": true,
|
|
||||||
})
|
|
||||||
|
|
||||||
scores[m.Prefix.Host] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const showLyrics = `my name is cleveland brown and I am proud to be
|
|
||||||
right back in my hometown with my new family.
|
|
||||||
there's old friends and new friends and even a bear.
|
|
||||||
through good times and bad times it's true love we share.
|
|
||||||
and so I found a place
|
|
||||||
where everyone will know
|
|
||||||
my happy mustache face
|
|
||||||
this is the cleveland show! haha!`
|
|
||||||
|
|
||||||
var lines = []string{
|
|
||||||
"my name is cleveland brown and I am proud to be",
|
|
||||||
"my name is cl3v3land brown and i am proud to be",
|
|
||||||
"right back in my hometown with my new family",
|
|
||||||
"right back in my hometown with my n3w family",
|
|
||||||
"there's old friends and new friends and even a bear",
|
|
||||||
"through good times and bad times it's true love we share.",
|
|
||||||
"and so I found a place",
|
|
||||||
"where everyone will know",
|
|
||||||
"my happy mustache face",
|
|
||||||
"this is the cleveland show! haha!",
|
|
||||||
}
|
|
||||||
|
|
||||||
var efknockr = []string{
|
|
||||||
"THIS NETWORK IS FUCKING BLOWJOBS LOL COME TO WORMSEC FOR SOME ICE COLD CHATS",
|
|
||||||
"0 DAY BANANA BOMBS \"OK\"",
|
|
||||||
"IRC.WORMSEC.US",
|
|
||||||
"THE HOTTEST MOST EXCLUSIVE SEC ON THE NET",
|
|
||||||
"THIS NETWORK IS BLOWJOBS! GET ON SUPERNETS FOR COLD HARD CHATS NOW",
|
|
||||||
"IRC.SUPERNETS.ORG | PORT 6667/6697 (SSL) | #SUPERBOWL | IPV6 READY",
|
|
||||||
"▓█▓▓▓▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▄░ ░▄▓▓▓▓▓▓▓▓▓█▓▓▓ IRC.WORMSEC.US | #SUPERBOWL",
|
|
||||||
"THIS NETWORK IS BLOWJOBS! GET ON SUPERNETS FOR COLD HARD CHATS NOW",
|
|
||||||
"▄",
|
|
||||||
"███▓▓▒▒▒▒▒▒▒░░░ ░░░░▒▒▒▓▓▓▓",
|
|
||||||
"▓█▓▓▓▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▄░ ░▄▓▓▓▓▓▓▓▓▓█▓▓▓",
|
|
||||||
"▒▓▓▓▓▒▒░░▒█▓▓▓▓▓▓▓▓▓▓█░▒░░▒▓▓▓▓▓▓▓▓▓▓▓█▓▓",
|
|
||||||
"░▒▓▓▒▒▒▒░░▒▒█▓▓▓▓▓▓▓▓▓█░▒░░░▒▓▓▓▓▓▓▓▓▓▓█▒▓░",
|
|
||||||
"▒▒▒▒▒▒▒▒▒▒▒░░▀▀▀▀▀▀▀ ░▒░░ ░▒▒▒▀▀▀▀▀▀▒▓▓▓▒",
|
|
||||||
"THE HOTTEST MOST EXCLUSIVE SEC ON THE NET",
|
|
||||||
"â–‘â–’â–“â–“â–’â–’â–’â–’â–‘â–‘â–’",
|
|
||||||
"Techman likes to fuck kids in the ass!!",
|
|
||||||
"https://discord.gg/3b86TH7",
|
|
||||||
"| |\\\\",
|
|
||||||
"/ \\ ||",
|
|
||||||
"( ,( )=m=D~~~ LOL DONGS",
|
|
||||||
"/ / | |",
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
cfg
|
|
|
@ -1,4 +0,0 @@
|
||||||
kcpd
|
|
||||||
====
|
|
||||||
|
|
||||||
A simple relay for multiplexing IRC sessions. Useful for bouncers or proxies.
|
|
|
@ -1,93 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
kcp "github.com/xtaci/kcp-go"
|
|
||||||
"github.com/xtaci/smux"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Client opens a TCP listener and forwards traffic to the remote server over KCP.
|
|
||||||
type Client struct {
|
|
||||||
cfg *Config
|
|
||||||
|
|
||||||
listener net.Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrBadConfig means the configuration is not correctly defined.
|
|
||||||
var ErrBadConfig = errors.New("kcpd: bad configuration file")
|
|
||||||
|
|
||||||
// NewClient constructs a new client with a given config.
|
|
||||||
func NewClient(cfg *Config) (*Client, error) {
|
|
||||||
if cfg.Mode != "client" {
|
|
||||||
return nil, ErrBadConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ClientServerAddress == "" && cfg.ClientUsername == "" && cfg.ClientPassword == "" && cfg.ClientBindaddr == "" {
|
|
||||||
return nil, ErrBadConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Client{cfg: cfg}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial blockingly connects to the remote server and relays TCP traffic.
|
|
||||||
func (c *Client) Dial() error {
|
|
||||||
conn, err := kcp.Dial(c.cfg.ClientServerAddress)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
tlsConn := tls.Client(conn, &tls.Config{
|
|
||||||
InsecureSkipVerify: true, // XXX hack please remove
|
|
||||||
})
|
|
||||||
defer tlsConn.Close()
|
|
||||||
|
|
||||||
session, err := smux.Client(tlsConn, smux.DefaultConfig())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
l, err := net.Listen("tcp", c.cfg.ClientBindaddr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer l.Close()
|
|
||||||
c.listener = l
|
|
||||||
|
|
||||||
for {
|
|
||||||
cconn, err := l.Accept()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
cstream, err := session.OpenStream()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
go copyConn(cconn, cstream)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close frees resouces acquired in the client.
|
|
||||||
func (c *Client) Close() error {
|
|
||||||
return c.listener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// copyConn copies one connection to another bidirectionally.
|
|
||||||
func copyConn(left, right net.Conn) error {
|
|
||||||
defer left.Close()
|
|
||||||
defer right.Close()
|
|
||||||
|
|
||||||
go io.Copy(left, right)
|
|
||||||
io.Copy(right, left)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
// gopreload.go
|
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is separate to make it very easy to both add into an application, but
|
|
||||||
also very easy to remove.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import _ "github.com/Xe/gopreload"
|
|
|
@ -1,13 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/google/gops/agent"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if err := agent.Listen(nil); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/caarlos0/env"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
|
||||||
yaml "gopkg.in/yaml.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config is the configuration for kcpd
|
|
||||||
type Config struct {
|
|
||||||
Mode string `env:"KCPD_MODE,required" envDefault:"server" yaml:"mode"`
|
|
||||||
|
|
||||||
// Client mode config
|
|
||||||
|
|
||||||
// What IP the client should connect to
|
|
||||||
ClientServerAddress string `env:"KCPD_SERVER_ADDRESS" yaml:"server"`
|
|
||||||
// Administrator's NickServ username
|
|
||||||
ClientUsername string `env:"KCPD_ADMIN_USERNAME" yaml:"admin_username"`
|
|
||||||
// Administrator's NickServ password
|
|
||||||
ClientPassword string `env:"KCPD_ADMIN_PASSWORD" yaml:"admin_password"`
|
|
||||||
// Local bindaddr
|
|
||||||
ClientBindaddr string `env:"KCPD_CLIENT_BINDADDR" yaml:"client_bind"`
|
|
||||||
|
|
||||||
// Server mode config
|
|
||||||
|
|
||||||
// What UDP port/address should kcpd bind on?
|
|
||||||
ServerBindAddr string `env:"KCPD_BIND" yaml:"bind"`
|
|
||||||
// Atheme URL for Nickserv authentication of the administrator for setting up KCP sessions
|
|
||||||
ServerAthemeURL string `env:"KCPD_ATHEME_URL" yaml:"atheme_url"`
|
|
||||||
// URL endpoint for allowing/denying users
|
|
||||||
ServerAllowListEndpoint string `env:"KCPD_ALLOWLIST_ENDPOINT" yaml:"allow_list_endpoint"`
|
|
||||||
// local ircd (unsecure) endpoint
|
|
||||||
ServerLocalIRCd string `env:"KCPD_LOCAL_IRCD" yaml:"local_ircd"`
|
|
||||||
// WEBIRC password to use for local sockets
|
|
||||||
ServerWEBIRCPassword string `env:"KCPD_WEBIRC_PASSWORD" yaml:"webirc_password"`
|
|
||||||
// ServerTLSCert is the TLS cert file
|
|
||||||
ServerTLSCert string `env:"KCPD_TLS_CERT" yaml:"tls_cert"`
|
|
||||||
// ServerTLSKey is the TLS key file
|
|
||||||
ServerTLSKey string `env:"KCPD_TLS_KEY" yaml:"tls_key"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
configFname = flag.String("config", "", "configuration file to use (if unset config will be pulled from the environment)")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
cfg := &Config{}
|
|
||||||
|
|
||||||
if *configFname != "" {
|
|
||||||
fin, err := os.Open(*configFname)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer fin.Close()
|
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(fin)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = yaml.Unmarshal(data, cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := env.Parse(cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch cfg.Mode {
|
|
||||||
case "client":
|
|
||||||
c, err := NewClient(cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
err = c.Dial()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "server":
|
|
||||||
s, err := NewServer(cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.ListenAndServe()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Fatal(ErrBadConfig)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
kcp "github.com/xtaci/kcp-go"
|
|
||||||
"github.com/xtaci/smux"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server represents the server side of kcpd. It listens on KCP and emits TCP connections from KCP streams.
|
|
||||||
type Server struct {
|
|
||||||
cfg *Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServer creates a new Server and validates config.
|
|
||||||
func NewServer(cfg *Config) (*Server, error) {
|
|
||||||
if cfg.Mode != "server" {
|
|
||||||
return nil, ErrBadConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ServerBindAddr == "" && cfg.ServerAthemeURL == "" && cfg.ServerAllowListEndpoint == "" && cfg.ServerLocalIRCd == "" && cfg.ServerWEBIRCPassword == "" && cfg.ServerTLSCert == "" && cfg.ServerTLSKey == "" {
|
|
||||||
return nil, ErrBadConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Server{cfg: cfg}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListenAndServe blockingly listens on the UDP port and relays KCP streams to TCP sockets.
|
|
||||||
func (s *Server) ListenAndServe() error {
|
|
||||||
l, err := kcp.Listen(s.cfg.ServerBindAddr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
log.Printf("listening on KCP: %v", l.Addr())
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn, err := l.Accept()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go s.handleConn(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleConn(conn net.Conn) error {
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
log.Printf("new client: %v", conn.RemoteAddr())
|
|
||||||
|
|
||||||
cert, err := tls.LoadX509KeyPair(s.cfg.ServerTLSCert, s.cfg.ServerTLSKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tcfg := &tls.Config{
|
|
||||||
InsecureSkipVerify: true, // XXX hack remove
|
|
||||||
Certificates: []tls.Certificate{cert},
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsConn := tls.Server(conn, tcfg)
|
|
||||||
defer tlsConn.Close()
|
|
||||||
|
|
||||||
session, err := smux.Server(tlsConn, smux.DefaultConfig())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
cstream, err := session.AcceptStream()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("client at %s error: %v", conn.RemoteAddr(), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ircConn, err := net.Dial("tcp", s.cfg.ServerLocalIRCd)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("client at %s error: %v", conn.RemoteAddr(), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
host, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
||||||
|
|
||||||
fmt.Fprintf(ircConn, "WEBIRC %s %s %s %s\r\n", s.cfg.ServerWEBIRCPassword, RandStringRunes(8), host, host)
|
|
||||||
|
|
||||||
go copyConn(cstream, ircConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
# Your stdlib service: xena/kcpdwhitelist
|
|
||||||
|
|
||||||
This is the README for your service.
|
|
||||||
|
|
||||||
A few notes;
|
|
||||||
|
|
||||||
`package.json` is NPM-compatible and contains some stdlib configuration details.
|
|
||||||
`.gitignore` has also been provided for your convenience.
|
|
||||||
|
|
||||||
# package.json
|
|
||||||
|
|
||||||
This is a standard `package.json`. You'll notice an additional `"stdlib"` field.
|
|
||||||
You can configure your service for the stdlib registry using;
|
|
||||||
|
|
||||||
`name` - The name to register on stdlib, in the format of `<username>/<service>`.
|
|
||||||
In order to compile to the registry you must have permission to compile to the
|
|
||||||
provided username's account.
|
|
||||||
|
|
||||||
`defaultFunction` - Execute if provided no function route (root service).
|
|
||||||
If not specified, your base service route will provide a list of available
|
|
||||||
functions in JSON format.
|
|
||||||
|
|
||||||
`timeout` - The time in ms at which to kill service execution. Free accounts are
|
|
||||||
limited to 30 seconds (30000).
|
|
||||||
|
|
||||||
`publish` - Whether to publish releases (versioned) to the stdlib public
|
|
||||||
directory. Packages pushed to the registry in non-release environments will
|
|
||||||
never be published.
|
|
||||||
|
|
||||||
# env.json
|
|
||||||
|
|
||||||
Environment configuration for your service. Each top level key (i.e.
|
|
||||||
`"dev"` and `"release"`) specifies their own set of key-value
|
|
||||||
pairs for a specific execution environment. The keys and values specified
|
|
||||||
are automatically added to the `process.env` variable in Node.js.
|
|
||||||
|
|
||||||
`"dev"` is the *non-configurable* name of the local environment, but can
|
|
||||||
also be used as an environment name for compilation
|
|
||||||
(i.e. `$ lib up development`).
|
|
||||||
|
|
||||||
`"release"` is the *non-configurable* name of the production environment when
|
|
||||||
you create releases with `$ lib release`.
|
|
||||||
|
|
||||||
You can add additional environments and key-value pairs, and use them for
|
|
||||||
compilation with `lib up <environment>`. Note that free accounts are
|
|
||||||
restricted to one compilation environment (aside from `"release"`).
|
|
||||||
|
|
||||||
*We recommend against checking this file in to version control*. It will be
|
|
||||||
saved with your tarball and is privately retrievable from the stdlib registry
|
|
||||||
using your account credentials. It has been added to `.gitignore` by default.
|
|
||||||
|
|
||||||
# f/main/function.json
|
|
||||||
|
|
||||||
This is your function definition file. The following fields can be used for
|
|
||||||
execution configuration of specific functions within your service.
|
|
||||||
|
|
||||||
`name` - The function name. This maps to an execution route over HTTP. For
|
|
||||||
example, `xena/kcpdwhitelist/main` would map to the first
|
|
||||||
function you've created.
|
|
||||||
|
|
||||||
`description` - A brief description of the function. To provide detailed
|
|
||||||
information about function execution, overwrite this README.
|
|
||||||
|
|
||||||
`args` - An `Array` describing each argument as you expect them to be passed to
|
|
||||||
`params.args`.
|
|
||||||
|
|
||||||
`kwargs` - An `Object` describing each keyword argument as you expect them to be
|
|
||||||
passed to `params.kwargs`
|
|
||||||
|
|
||||||
`http` - Information to provide to function requests over HTTP.
|
|
||||||
|
|
||||||
`http.headers` - HTTP headers to return in the response. Examples are
|
|
||||||
`"Content-Type"` to specify file type if your function returns a `Buffer` or
|
|
||||||
`"Access-Control-Allow-Origin"` to restrict browser-based function requests.
|
|
||||||
|
|
||||||
# f/main/index.js
|
|
||||||
|
|
||||||
The entry point to your function described in `f/main/function.json`.
|
|
||||||
This is *non-configurable*. You may add as many subdirectories and supportive
|
|
||||||
files as you like, but `index.js` will remain the entry point and *must*
|
|
||||||
export a function to be active.
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"dev": {
|
|
||||||
"key": "value"
|
|
||||||
},
|
|
||||||
"release": {
|
|
||||||
"key": "value"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
{
|
|
||||||
"name": "main",
|
|
||||||
"description": "Function",
|
|
||||||
"args": [
|
|
||||||
"First argument",
|
|
||||||
"Second argument"
|
|
||||||
],
|
|
||||||
"kwargs": {
|
|
||||||
"alpha": "Keyword argument alpha",
|
|
||||||
"beta": "Keyword argument beta"
|
|
||||||
},
|
|
||||||
"http": {
|
|
||||||
"headers": {}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/* Import dependencies, declare constants */
|
|
||||||
|
|
||||||
win = (callback) => {
|
|
||||||
callback(null, "allowed");
|
|
||||||
};
|
|
||||||
|
|
||||||
fail = (callback) => {
|
|
||||||
callback("not allowed");
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Your function call
|
|
||||||
* @param {Object} params Execution parameters
|
|
||||||
* Members
|
|
||||||
* - {Array} args Arguments passed to function
|
|
||||||
* - {Object} kwargs Keyword arguments (key-value pairs) passed to function
|
|
||||||
* - {String} remoteAddress The IPv4 or IPv6 address of the caller
|
|
||||||
*
|
|
||||||
* @param {Function} callback Execute this to end the function call
|
|
||||||
* Arguments
|
|
||||||
* - {Error} error The error to show if function fails
|
|
||||||
* - {Any} returnValue JSON serializable (or Buffer) return value
|
|
||||||
*/
|
|
||||||
module.exports = (params, callback) => {
|
|
||||||
switch (params.kwargs.user) {
|
|
||||||
case "Xena":
|
|
||||||
win(callback);
|
|
||||||
|
|
||||||
default:
|
|
||||||
fail(callback);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"name": "kcpdwhitelist",
|
|
||||||
"version": "0.0.2",
|
|
||||||
"description": "Service",
|
|
||||||
"author": "xena <me@christine.website>",
|
|
||||||
"main": "functions/main/index.js",
|
|
||||||
"dependencies": {},
|
|
||||||
"private": true,
|
|
||||||
"stdlib": {
|
|
||||||
"name": "xena/kcpdwhitelist",
|
|
||||||
"defaultFunction": "main",
|
|
||||||
"timeout": 10000,
|
|
||||||
"publish": true,
|
|
||||||
"personalize": {
|
|
||||||
"keys": [],
|
|
||||||
"user": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
}
|
|
||||||
|
|
||||||
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
||||||
|
|
||||||
// RandStringRunes creates a new random string of length n.
|
|
||||||
func RandStringRunes(n int) string {
|
|
||||||
b := make([]rune, n)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
|
||||||
}
|
|
||||||
return string(b)
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
9474f19b515f52326c7d197d2d097caa7fc7485e github.com/caarlos0/env
|
|
||||||
4ed13390c0acd2ff4e371e64d8b97c8954138243 github.com/joho/godotenv
|
|
||||||
4ed13390c0acd2ff4e371e64d8b97c8954138243 github.com/joho/godotenv/autoload
|
|
||||||
09cded8978dc9e80714c4d85b0322337b0a1e5e0 github.com/klauspost/cpuid
|
|
||||||
5abf0ee302ccf4834e84f63ff74eca3e8b88e4e2 github.com/klauspost/reedsolomon
|
|
||||||
ff09b135c25aae272398c51a07235b90a75aa4f0 github.com/pkg/errors
|
|
||||||
f75e45921594f847a073eb5637b01a8fe5d21e43 github.com/xtaci/kcp-go
|
|
||||||
a1a5df8f92af764f378f07d6a3dd8eb3f7aa190a github.com/xtaci/smux
|
|
||||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/blowfish
|
|
||||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/cast5
|
|
||||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/pbkdf2
|
|
||||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/salsa20
|
|
||||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/salsa20/salsa
|
|
||||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/tea
|
|
||||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/twofish
|
|
||||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/xtea
|
|
||||||
ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d golang.org/x/net/bpf
|
|
||||||
ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d golang.org/x/net/internal/iana
|
|
||||||
ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d golang.org/x/net/internal/netreflect
|
|
||||||
ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d golang.org/x/net/ipv4
|
|
||||||
9f9df34309c04878acc86042b16630b0f696e1de (dirty) gopkg.in/yaml.v1
|
|
||||||
a00a8beb369cafd88bb7b32f31fc4ff3219c3565 github.com/Xe/gopreload
|
|
||||||
62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/agent
|
|
||||||
62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/internal
|
|
||||||
62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/signal
|
|
||||||
c2c54e542fb797ad986b31721e1baedf214ca413 github.com/kardianos/osext
|
|
|
@ -1 +0,0 @@
|
||||||
splatbot
|
|
|
@ -1,6 +0,0 @@
|
||||||
FROM golang:1.4.2
|
|
||||||
|
|
||||||
ADD . /go/src/github.com/Xe/tools/irc/splatbot
|
|
||||||
RUN go get github.com/Xe/tools/irc/splatbot/...
|
|
||||||
|
|
||||||
CMD splatbot
|
|
|
@ -1,4 +0,0 @@
|
||||||
/*
|
|
||||||
Command splatbot is a simple IRC bot that lets you see the latest splatoon maps.
|
|
||||||
*/
|
|
||||||
package main
|
|
|
@ -1,53 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/belak/irc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func handlePrivmsg(c *irc.Client, e *irc.Event) {
|
|
||||||
if strings.HasPrefix(e.Trailing(), "!splatoon") {
|
|
||||||
splatoonLookup(c, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func splatoonLookup(c *irc.Client, e *irc.Event) {
|
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
c.Reply(e, "Couldn't look up splatoon maps: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
c.Reply(e, "Couldn't look up splatoon maps: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
var sd []SplatoonData
|
|
||||||
err = json.Unmarshal(body, &sd)
|
|
||||||
if err != nil {
|
|
||||||
c.Reply(e, "Couldn't look up splatoon maps: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data := sd[0]
|
|
||||||
|
|
||||||
stage1 := data.Stages[0]
|
|
||||||
stage2 := data.Stages[1]
|
|
||||||
c.Reply(
|
|
||||||
e,
|
|
||||||
"From %s to %s, the stage rotation is %s and %s",
|
|
||||||
data.DatetimeTermBegin, data.DatetimeTermEnd,
|
|
||||||
englishIfy(stage1), englishIfy(stage2),
|
|
||||||
)
|
|
||||||
|
|
||||||
log.Printf("%s asked me to look up data", e.Identity.Nick)
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/belak/irc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
nick = flag.String("nick", "Inkling", "Nickname to use")
|
|
||||||
user = flag.String("user", "xena", "Username to use")
|
|
||||||
channels = flag.String("channels", "#niichan", "Comma-separated list of channels to join")
|
|
||||||
server = flag.String("server", "irc.ponychat.net", "Server to connect to")
|
|
||||||
port = flag.Int("port", 6697, "port to connect to")
|
|
||||||
ssl = flag.Bool("ssl", true, "Use ssl?")
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.Parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
handler := irc.NewBasicMux()
|
|
||||||
|
|
||||||
client := irc.NewClient(irc.HandlerFunc(handler.HandleEvent), *nick, *user, "SplatBot by Xena", "")
|
|
||||||
|
|
||||||
handler.Event("001", func(c *irc.Client, e *irc.Event) {
|
|
||||||
c.Writef("MODE %s +B", c.CurrentNick())
|
|
||||||
c.Writef("JOIN %s", *channels)
|
|
||||||
})
|
|
||||||
|
|
||||||
handler.Event("PRIVMSG", handlePrivmsg)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if *ssl {
|
|
||||||
err = client.DialTLS(fmt.Sprintf("%s:%d", *server, *port), nil)
|
|
||||||
} else {
|
|
||||||
err = client.Dial(fmt.Sprintf("%s:%d", *server, *port))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
const url = "https://s3-ap-northeast-1.amazonaws.com/splatoon-data.nintendo.net/stages_info.json"
|
|
||||||
|
|
||||||
var mapNames map[string]string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
mapNames = map[string]string{
|
|
||||||
"eefffc33ee1956b7b70e250d5835aa67be9152d42bc76aa8874987ebdfc19944": "Urchin Underpass",
|
|
||||||
"b8067d2839476ec39072e371b4c59fa85454cdb618515af080ca6080772f3264": "Port Mackerel",
|
|
||||||
"50c01bca5b3117f4f7893df86d2e2d95435dbb1aae1da6831b8e74838859bc7d": "Saltspray Rig",
|
|
||||||
"9a1736540c3fde7e409cb9c7e333441157d88dfe8ce92bc6aafcb9f79c56cb3d": "Blackbelly Skatepark",
|
|
||||||
"d7bf0ca4466e980f994ef7b32faeb71d80611a28c5b9feef84a00e3c4c9d7bc1": "Walleye Warehouse",
|
|
||||||
"8c69b7c9a81369b5cfd22adbf41c13a8df01969ff3d0e531a8bcb042156bc549": "Arowana Mall",
|
|
||||||
"1ac0981d03c18576d9517f40461b66a472168a8f14f6a8714142af9805df7b8c": "Bluefin Depot",
|
|
||||||
"c52a7ab7202a576ee18d94be687d97190e90fdcc25fc4b1591c1a8e0c1c299a5": "Kelp Dome",
|
|
||||||
"6a6c3a958712adedcceb34f719e220ab0d840d8753e5f51b089d363bd1763c91": "Moray Towers",
|
|
||||||
"a54716422edf71ac0b3d20fbb4ba5970a7a78ba304fcf935aaf69254d61ca709": "Flounder Heights",
|
|
||||||
"fafe7416d363c7adc8c5c7b0f76586216ba86dcfe3fd89708d672e99bc822adc": "Camp Triggerfish",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func englishIfy(s *Stage) string {
|
|
||||||
name, ok := mapNames[s.ID]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Sprintf("Please tell Xena the name of stage id \"%s\" with jpn name %s", s.ID, s.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return name
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
type SplatoonData struct {
|
|
||||||
DatetimeTermBegin string `json:"datetime_term_begin"`
|
|
||||||
DatetimeTermEnd string `json:"datetime_term_end"`
|
|
||||||
Stages []*Stage `json:"stages"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Stage struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
smartbot
|
|
||||||
*.gob
|
|
|
@ -1,87 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/thoj/go-ircevent"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
brainInput = flag.String("brain", "", "brain file")
|
|
||||||
lastSpoken time.Time
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
chain := NewChain(3)
|
|
||||||
|
|
||||||
if *brainInput != "" {
|
|
||||||
log.Printf("Opening %s...", *brainInput)
|
|
||||||
|
|
||||||
fin, err := os.Open(*brainInput)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := bufio.NewScanner(fin)
|
|
||||||
for s.Scan() {
|
|
||||||
t := s.Text()
|
|
||||||
|
|
||||||
_, err := chain.Write(t)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = chain.Save("mybrain-pc.gob")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := chain.Load("mybrain-pc.gob")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
|
|
||||||
conn := irc.IRC("BeefSupreme", "Doritos")
|
|
||||||
|
|
||||||
err := conn.Connect("irc.ponychat.net:6667")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.AddCallback("001", func(e *irc.Event) {
|
|
||||||
conn.Join("#geek")
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.AddCallback("PRIVMSG", func(e *irc.Event) {
|
|
||||||
log.Printf("writing brain with %s", e.Arguments[1])
|
|
||||||
chain.Write(e.Arguments[1])
|
|
||||||
chain.Save("mybrain-pc.gob")
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.AddCallback("PRIVMSG", func(e *irc.Event) {
|
|
||||||
if lastSpoken.Add(5 * time.Minute).Before(time.Now()) {
|
|
||||||
log.Println("It's been long enough that I can speak!")
|
|
||||||
|
|
||||||
if rand.Int()%4 == 2 {
|
|
||||||
log.Printf("About to say something...")
|
|
||||||
time.Sleep(time.Duration((rand.Int()%15)+4) * time.Second)
|
|
||||||
conn.Privmsg(e.Arguments[0], chain.Generate(15))
|
|
||||||
|
|
||||||
lastSpoken = time.Now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.Loop()
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
// This Markov chain code is taken from the "Generating arbitrary text"
|
|
||||||
// codewalk: http://golang.org/doc/codewalk/markov/
|
|
||||||
//
|
|
||||||
// Minor modifications have been made to make it easier to integrate
|
|
||||||
// with a webserver and to save/load state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/gob"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Prefix is a Markov chain prefix of one or more words.
|
|
||||||
type Prefix []string
|
|
||||||
|
|
||||||
// String returns the Prefix as a string (for use as a map key).
|
|
||||||
func (p Prefix) String() string {
|
|
||||||
return strings.Join(p, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shift removes the first word from the Prefix and appends the given word.
|
|
||||||
func (p Prefix) Shift(word string) {
|
|
||||||
copy(p, p[1:])
|
|
||||||
p[len(p)-1] = word
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chain contains a map ("chain") of prefixes to a list of suffixes.
|
|
||||||
// A prefix is a string of prefixLen words joined with spaces.
|
|
||||||
// A suffix is a single word. A prefix can have multiple suffixes.
|
|
||||||
type Chain struct {
|
|
||||||
Chain map[string][]string
|
|
||||||
prefixLen int
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewChain returns a new Chain with prefixes of prefixLen words.
|
|
||||||
func NewChain(prefixLen int) *Chain {
|
|
||||||
return &Chain{
|
|
||||||
Chain: make(map[string][]string),
|
|
||||||
prefixLen: prefixLen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write parses the bytes into prefixes and suffixes that are stored in Chain.
|
|
||||||
func (c *Chain) Write(in string) (int, error) {
|
|
||||||
sr := strings.NewReader(in)
|
|
||||||
p := make(Prefix, c.prefixLen)
|
|
||||||
for {
|
|
||||||
var s string
|
|
||||||
if _, err := fmt.Fscan(sr, &s); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
key := p.String()
|
|
||||||
c.mu.Lock()
|
|
||||||
c.Chain[key] = append(c.Chain[key], s)
|
|
||||||
c.mu.Unlock()
|
|
||||||
p.Shift(s)
|
|
||||||
}
|
|
||||||
return len(in), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate returns a string of at most n words generated from Chain.
|
|
||||||
func (c *Chain) Generate(n int) string {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
p := make(Prefix, c.prefixLen)
|
|
||||||
var words []string
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
choices := c.Chain[p.String()]
|
|
||||||
if len(choices) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
next := choices[rand.Intn(len(choices))]
|
|
||||||
words = append(words, next)
|
|
||||||
p.Shift(next)
|
|
||||||
}
|
|
||||||
return strings.Join(words, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the chain to a file
|
|
||||||
func (c *Chain) Save(fileName string) error {
|
|
||||||
// Open the file for writing
|
|
||||||
fo, err := os.Create(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// close fo on exit and check for its returned error
|
|
||||||
defer func() {
|
|
||||||
if err := fo.Close(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Create an encoder and dump to it
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
|
|
||||||
enc := gob.NewEncoder(fo)
|
|
||||||
err = enc.Encode(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the chain from a file
|
|
||||||
func (c *Chain) Load(fileName string) error {
|
|
||||||
// Open the file for reading
|
|
||||||
fi, err := os.Open(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// close fi on exit and check for its returned error
|
|
||||||
defer func() {
|
|
||||||
if err := fi.Close(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Create a decoder and read from it
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
|
|
||||||
dec := gob.NewDecoder(fi)
|
|
||||||
err = dec.Decode(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
almarid
|
|
||||||
.env
|
|
|
@ -1,13 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/google/gops/agent"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if err := agent.Listen(nil); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/McKael/madon"
|
|
||||||
"github.com/Xe/ln"
|
|
||||||
"github.com/caarlos0/env"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cfg = &struct {
|
|
||||||
Instance string `env:"INSTANCE,required"`
|
|
||||||
AppID string `env:"APP_ID,required"`
|
|
||||||
AppSecret string `env:"APP_SECRET,required"`
|
|
||||||
Token string `env:"TOKEN,required"`
|
|
||||||
WordFile string `env:"WORD_FILE,required"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
err := env.Parse(cfg)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "env.Parse"})
|
|
||||||
}
|
|
||||||
|
|
||||||
fin, err := os.Open(cfg.WordFile)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "os.Open(cfg.WordFile)"})
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(fin)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "ioutil.ReadAll(fin)"})
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := madon.RestoreApp("almarid:", cfg.Instance, cfg.AppID, cfg.AppSecret, &madon.UserToken{AccessToken: cfg.Token})
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "madon.RestoreApp"})
|
|
||||||
}
|
|
||||||
_ = c
|
|
||||||
|
|
||||||
lines := bytes.Split(data, []byte("\n"))
|
|
||||||
words := []string{}
|
|
||||||
|
|
||||||
for _, line := range lines {
|
|
||||||
if len(line) > 5 {
|
|
||||||
word := string(line)
|
|
||||||
|
|
||||||
if strings.HasPrefix(word, "'") {
|
|
||||||
word = word[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
words = append(words, word)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ln.Log(ln.F{"action": "words.loaded", "count": len(words)})
|
|
||||||
|
|
||||||
lenBig := big.NewInt(int64(len(words)))
|
|
||||||
|
|
||||||
first := true
|
|
||||||
|
|
||||||
for {
|
|
||||||
bi, err := rand.Int(rand.Reader, lenBig)
|
|
||||||
if err != nil {
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "big.Rand",
|
|
||||||
"err": err,
|
|
||||||
})
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
i := int(bi.Int64())
|
|
||||||
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
} else {
|
|
||||||
time.Sleep(5 * time.Minute)
|
|
||||||
}
|
|
||||||
|
|
||||||
txt := fmt.Sprintf("%s is not doing, allah is doing", words[i])
|
|
||||||
|
|
||||||
st, err := c.PostStatus(txt, 0, nil, false, "", "public")
|
|
||||||
if err != nil {
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"err": err,
|
|
||||||
"action": "c.PostStatus",
|
|
||||||
"text": txt,
|
|
||||||
})
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "tooted",
|
|
||||||
"text": txt,
|
|
||||||
"id": st.ID,
|
|
||||||
"url": st.URL,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,2 +0,0 @@
|
||||||
.env
|
|
||||||
furrybb
|
|
|
@ -1,37 +0,0 @@
|
||||||
furrybb
|
|
||||||
======
|
|
||||||
|
|
||||||
This boosts any toots with the tag `#furry`, but this can be used for other
|
|
||||||
hashtags too. Usage is simple:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ go get github.com/Xe/x/mastodon/furrybb
|
|
||||||
$ cd $GOPATH/src/github.com/Xe/x/mastodon/furrybb
|
|
||||||
$ go get github.com/Xe/x/mastodon/mkapp
|
|
||||||
$ mkapp
|
|
||||||
Usage of [mkapp]:
|
|
||||||
-app-name string
|
|
||||||
app name for mastodon (default "Xe/x bot")
|
|
||||||
-instance string
|
|
||||||
mastodon instance
|
|
||||||
-password string
|
|
||||||
password to generate token
|
|
||||||
-redirect-uri string
|
|
||||||
redirect URI for app users (default "urn:ietf:wg:oauth:2.0:oob")
|
|
||||||
-username string
|
|
||||||
username to generate token
|
|
||||||
-website string
|
|
||||||
website for users that click the app name (default "https://github.com/Xe/x")
|
|
||||||
exit status 2
|
|
||||||
$ mkapp [your options here] > .env
|
|
||||||
$ echo "HASHTAG=furry" >> .env
|
|
||||||
$ go build && ./furrybb
|
|
||||||
```
|
|
||||||
|
|
||||||
once you see:
|
|
||||||
|
|
||||||
```
|
|
||||||
time="2017-04-22T19:25:28-07:00" action=streaming.toots
|
|
||||||
```
|
|
||||||
|
|
||||||
you're all good fam.
|
|
|
@ -1,9 +0,0 @@
|
||||||
// gopreload.go
|
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is separate to make it very easy to both add into an application, but
|
|
||||||
also very easy to remove.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import _ "github.com/Xe/gopreload"
|
|
|
@ -1,13 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/google/gops/agent"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if err := agent.Listen(nil); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/McKael/madon"
|
|
||||||
"github.com/Xe/ln"
|
|
||||||
"github.com/caarlos0/env"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cfg = &struct {
|
|
||||||
Instance string `env:"INSTANCE,required"`
|
|
||||||
AppID string `env:"APP_ID,required"`
|
|
||||||
AppSecret string `env:"APP_SECRET,required"`
|
|
||||||
Token string `env:"TOKEN,required"`
|
|
||||||
Hashtag string `env:"HASHTAG,required"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
var scopes = []string{"read", "write", "follow"}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
err := env.Parse(cfg)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "startup"})
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := madon.RestoreApp("furry boostbot", cfg.Instance, cfg.AppID, cfg.AppSecret, &madon.UserToken{AccessToken: cfg.Token})
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "madon.RestoreApp"})
|
|
||||||
}
|
|
||||||
|
|
||||||
evChan := make(chan madon.StreamEvent, 10)
|
|
||||||
stop := make(chan bool)
|
|
||||||
done := make(chan bool)
|
|
||||||
|
|
||||||
err = c.StreamListener("public", "", evChan, stop, done)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "c.StreamListener"})
|
|
||||||
}
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "streaming.toots",
|
|
||||||
})
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case _, ok := <-done:
|
|
||||||
if !ok {
|
|
||||||
ln.Fatal(ln.F{"action": "stream.dead"})
|
|
||||||
}
|
|
||||||
|
|
||||||
case ev := <-evChan:
|
|
||||||
switch ev.Event {
|
|
||||||
case "error":
|
|
||||||
ln.Fatal(ln.F{"err": ev.Error, "action": "processing.event"})
|
|
||||||
case "update":
|
|
||||||
s := ev.Data.(madon.Status)
|
|
||||||
|
|
||||||
for _, tag := range s.Tags {
|
|
||||||
if tag.Name == cfg.Hashtag {
|
|
||||||
err = c.ReblogStatus(s.ID)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "c.ReblogStatus", "id": s.ID})
|
|
||||||
}
|
|
||||||
|
|
||||||
ln.Log(ln.F{
|
|
||||||
"action": "reblogged",
|
|
||||||
"id": s.ID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
039943f9c38eb1f43c26e1ffd21ad691efc00657 github.com/McKael/madon
|
|
||||||
f759b797c0ff6b2c514202198fe5e8ba90094c14 github.com/Xe/ln
|
|
||||||
9474f19b515f52326c7d197d2d097caa7fc7485e github.com/caarlos0/env
|
|
||||||
e8f0f8aaa98dfb6586cbdf2978d511e3199a960a github.com/gorilla/websocket
|
|
||||||
726cc8b906e3d31c70a9671c90a13716a8d3f50d github.com/joho/godotenv
|
|
||||||
726cc8b906e3d31c70a9671c90a13716a8d3f50d github.com/joho/godotenv/autoload
|
|
||||||
248dadf4e9068a0b3e79f02ed0a610d935de5302 github.com/pkg/errors
|
|
||||||
14de1ac72d9ae5c3c0d7c02164c52ebd3b951a4e github.com/sendgrid/rest
|
|
||||||
a00a8beb369cafd88bb7b32f31fc4ff3219c3565 github.com/Xe/gopreload
|
|
||||||
62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/agent
|
|
||||||
62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/internal
|
|
||||||
62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/signal
|
|
||||||
c2c54e542fb797ad986b31721e1baedf214ca413 github.com/kardianos/osext
|
|
|
@ -1,36 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/McKael/madon"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
instance = flag.String("instance", "", "mastodon instance")
|
|
||||||
appName = flag.String("app-name", "Xe/x bot", "app name for mastodon")
|
|
||||||
redirectURI = flag.String("redirect-uri", "urn:ietf:wg:oauth:2.0:oob", "redirect URI for app users")
|
|
||||||
website = flag.String("website", "https://github.com/Xe/x", "website for users that click the app name")
|
|
||||||
username = flag.String("username", "", "username to generate token")
|
|
||||||
password = flag.String("password", "", "password to generate token")
|
|
||||||
)
|
|
||||||
|
|
||||||
var scopes = []string{"read", "write", "follow"}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
c, err := madon.NewApp(*appName, scopes, *redirectURI, *instance)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.LoginBasic(*username, *password, scopes)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("APP_ID=%s\nAPP_SECRET=%s\nTOKEN=%s\nINSTANCE=%s", c.ID, c.Secret, c.UserToken.AccessToken, *instance)
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/justinian/dice"
|
|
||||||
"github.com/syfaro/finch"
|
|
||||||
"gopkg.in/telegram-bot-api.v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
finch.RegisterCommand(&diceCommand{})
|
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
type diceCommand struct {
|
|
||||||
finch.CommandBase
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *diceCommand) Help() finch.Help {
|
|
||||||
return finch.Help{
|
|
||||||
Name: "Dice",
|
|
||||||
Description: "Standard: xdy[[k|d][h|l]z][+/-c] - rolls and sums x y-sided dice, keeping or dropping the lowest or highest z dice and optionally adding or subtracting c.",
|
|
||||||
Example: "/dice@@ 4d6kh3+4",
|
|
||||||
Botfather: [][]string{
|
|
||||||
[]string{"dice", "4d20, etc"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *diceCommand) ShouldExecute(message tgbotapi.Message) bool {
|
|
||||||
return finch.SimpleCommand("dice", message.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *diceCommand) Execute(message tgbotapi.Message) error {
|
|
||||||
parv := strings.Fields(message.CommandArguments())
|
|
||||||
|
|
||||||
if len(parv) != 1 {
|
|
||||||
msg := tgbotapi.NewMessage(message.Chat.ID, "Try something like 4d20")
|
|
||||||
return cmd.Finch.SendMessage(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
text, _, err := dice.Roll(parv[0])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := tgbotapi.NewMessage(message.Chat.ID, text.String())
|
|
||||||
|
|
||||||
return cmd.Finch.SendMessage(msg)
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/syfaro/finch"
|
|
||||||
"gopkg.in/telegram-bot-api.v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
finch.RegisterCommand(&printerfactCommand{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type printerfactCommand struct {
|
|
||||||
finch.CommandBase
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *printerfactCommand) Help() finch.Help {
|
|
||||||
return finch.Help{
|
|
||||||
Name: "Printerfact",
|
|
||||||
Description: "Displays printerfactrmation about the currently requesting user",
|
|
||||||
Example: "/printerfact@@",
|
|
||||||
Botfather: [][]string{
|
|
||||||
[]string{"printerfact", "Printerfactrmation about the current user"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *printerfactCommand) ShouldExecute(message tgbotapi.Message) bool {
|
|
||||||
return finch.SimpleCommand("printerfact", message.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *printerfactCommand) Execute(message tgbotapi.Message) error {
|
|
||||||
resp, err := http.Get("http://xena.stdlib.com/printerfacts")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
factStruct := &struct {
|
|
||||||
Facts []string `json:"facts"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
json.Unmarshal(body, factStruct)
|
|
||||||
|
|
||||||
text := fmt.Sprintf("%s", factStruct.Facts[0])
|
|
||||||
|
|
||||||
msg := tgbotapi.NewMessage(message.Chat.ID, text)
|
|
||||||
|
|
||||||
return cmd.Finch.SendMessage(msg)
|
|
||||||
}
|
|
|
@ -1,104 +0,0 @@
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/syfaro/finch"
|
|
||||||
"gopkg.in/telegram-bot-api.v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
finch.RegisterCommand(&splattusCommand{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type splattusCommand struct {
|
|
||||||
finch.CommandBase
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *splattusCommand) Help() finch.Help {
|
|
||||||
return finch.Help{
|
|
||||||
Name: "Splattus",
|
|
||||||
Description: "Displays splatoon 2 status",
|
|
||||||
Example: "/splattus@@",
|
|
||||||
Botfather: [][]string{
|
|
||||||
[]string{"splattus", "Splatoon 2 map rotations"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *splattusCommand) ShouldExecute(message tgbotapi.Message) bool {
|
|
||||||
return finch.SimpleCommand("splattus", message.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *splattusCommand) Execute(message tgbotapi.Message) error {
|
|
||||||
resp, err := http.Get("https://splatoon.ink/schedule2")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
st := &splattus{}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
json.Unmarshal(body, st)
|
|
||||||
|
|
||||||
modeInfo := []string{st.Modes.Regular[1].String(), st.Modes.Gachi[1].String(), st.Modes.League[1].String()}
|
|
||||||
text := strings.Join(modeInfo, "\n")
|
|
||||||
|
|
||||||
msg := tgbotapi.NewMessage(message.Chat.ID, text)
|
|
||||||
|
|
||||||
return cmd.Finch.SendMessage(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
type splatoonMode struct {
|
|
||||||
StartTime int64 `json:"startTime"`
|
|
||||||
EndTime int64 `json:"endTime"`
|
|
||||||
Maps []string `json:"maps"`
|
|
||||||
Rule splatoonRule `json:"rule"`
|
|
||||||
Mode splatoonGameMode `json:"mode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm splatoonMode) String() string {
|
|
||||||
maps := strings.Join(sm.Maps, ", ")
|
|
||||||
end := time.Unix(sm.EndTime, 0)
|
|
||||||
now := time.Now()
|
|
||||||
diff := end.Sub(now)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s:\nRotation ends at %s (in %s)\nMaps: %s\nRule: %s\n", sm.Mode, end.Format(time.RFC3339), diff, maps, sm.Rule)
|
|
||||||
}
|
|
||||||
|
|
||||||
type splatoonGameMode struct {
|
|
||||||
Key string `json:"key"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sgm splatoonGameMode) String() string {
|
|
||||||
return sgm.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
type splatoonRule struct {
|
|
||||||
Key string `json:"key"`
|
|
||||||
MultilineName string `json:"multiline_name"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sr splatoonRule) String() string {
|
|
||||||
return sr.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
type splattus struct {
|
|
||||||
UpdateTime int64 `json:"updateTime"`
|
|
||||||
Modes struct {
|
|
||||||
League []splatoonMode `json:"league"`
|
|
||||||
Regular []splatoonMode `json:"regular"`
|
|
||||||
Gachi []splatoonMode `json:"gachi"`
|
|
||||||
} `json:"modes"`
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
_ "github.com/Xe/x/tg/covfefe/commands"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
|
||||||
"github.com/syfaro/finch"
|
|
||||||
_ "github.com/syfaro/finch/commands/help"
|
|
||||||
_ "github.com/syfaro/finch/commands/info"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
f := finch.NewFinch(os.Getenv("TELEGRAM_TOKEN"))
|
|
||||||
|
|
||||||
f.Start()
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
a9c833d2837d3b16888d55d5aafa9ffe9afb22b0 github.com/certifi/gocertifi
|
|
||||||
d175f85701dfbf44cb0510114c9943e665e60907 github.com/getsentry/raven-go
|
|
||||||
726cc8b906e3d31c70a9671c90a13716a8d3f50d github.com/joho/godotenv
|
|
||||||
726cc8b906e3d31c70a9671c90a13716a8d3f50d github.com/joho/godotenv/autoload
|
|
||||||
3d2cfac89f5f4023604c963214759ea5f5d33972 github.com/justinian/dice
|
|
||||||
1fa76025c307b26485b59eaa8bdfb4866cbee29f github.com/syfaro/finch
|
|
||||||
1fa76025c307b26485b59eaa8bdfb4866cbee29f github.com/syfaro/finch/commands/help
|
|
||||||
1fa76025c307b26485b59eaa8bdfb4866cbee29f github.com/syfaro/finch/commands/info
|
|
||||||
a90a01d73ae432e2611d178c18367fbaa13e0154 github.com/technoweenie/multipartstreamer
|
|
||||||
0a57807db79efce7f6719fbb2c0e0f83fda79aec gopkg.in/telegram-bot-api.v4
|
|
|
@ -1,2 +0,0 @@
|
||||||
.env
|
|
||||||
meirl
|
|
|
@ -1,9 +0,0 @@
|
||||||
// gopreload.go
|
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is separate to make it very easy to both add into an application, but
|
|
||||||
also very easy to remove.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import _ "github.com/Xe/gopreload"
|
|
|
@ -1,13 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/google/gops/agent"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if err := agent.Listen(nil); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/McKael/madon"
|
|
||||||
"github.com/Xe/ln"
|
|
||||||
"github.com/caarlos0/env"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
|
||||||
"github.com/turnage/graw"
|
|
||||||
"github.com/turnage/graw/reddit"
|
|
||||||
"gopkg.in/telegram-bot-api.v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
const appid = "github.com/Xe/x/tg/meirl"
|
|
||||||
const version = "0.1-dev"
|
|
||||||
|
|
||||||
type config struct {
|
|
||||||
RedditBotAdmin string `env:"REDDIT_ADMIN_USERNAME,required"`
|
|
||||||
Subreddits []string `env:"SUBREDDITS,required"`
|
|
||||||
|
|
||||||
TelegramToken string `env:"TELEGRAM_TOKEN,required"`
|
|
||||||
TelegramAdmin string `env:"TELEGRAM_ADMIN,required"`
|
|
||||||
TelegramChannelID int64 `env:"TELEGRAM_CHANNEL_ID,required"`
|
|
||||||
|
|
||||||
MastodonToken string `env:"MASTODON_TOKEN,required"`
|
|
||||||
MastodonClientSecret string `env:"MASTODON_CLIENT_SECRET,required"`
|
|
||||||
MastodonClientID string `env:"MASTODON_CLIENT_ID,required"`
|
|
||||||
MastodonInstance string `env:"MASTODON_INSTANCE,required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var cfg config
|
|
||||||
err := env.Parse(&cfg)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "env.Parse"})
|
|
||||||
}
|
|
||||||
|
|
||||||
tg, err := tgbotapi.NewBotAPI(cfg.TelegramToken)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "tgbotapi.NewBotAPI"})
|
|
||||||
}
|
|
||||||
|
|
||||||
ln.Log(ln.F{"action": "telegram_active", "username": tg.Self.UserName})
|
|
||||||
|
|
||||||
userAgent := fmt.Sprintf(
|
|
||||||
"%s on %s %s:%s:%s (by /u/%s)",
|
|
||||||
runtime.Version(), runtime.GOOS, runtime.GOARCH,
|
|
||||||
appid, version, cfg.RedditBotAdmin,
|
|
||||||
)
|
|
||||||
|
|
||||||
rd, err := reddit.NewScript(userAgent, 5*time.Second)
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "user_agent": userAgent})
|
|
||||||
}
|
|
||||||
_ = rd
|
|
||||||
|
|
||||||
ln.Log(ln.F{"action": "reddit_connected", "user_agent": userAgent})
|
|
||||||
|
|
||||||
md, err := madon.RestoreApp("me_irl", cfg.MastodonInstance, cfg.MastodonClientID, cfg.MastodonClientSecret, &madon.UserToken{AccessToken: cfg.MastodonToken})
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "madon.RestoreApp"})
|
|
||||||
}
|
|
||||||
|
|
||||||
a := &announcer{
|
|
||||||
cfg: &cfg,
|
|
||||||
tg: tg,
|
|
||||||
md: md,
|
|
||||||
}
|
|
||||||
|
|
||||||
stop, wait, err := graw.Scan(a, rd, graw.Config{Subreddits: cfg.Subreddits, Logger: log.New(os.Stderr, "", log.LstdFlags)})
|
|
||||||
if err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "graw.Scan", "subreddits": cfg.Subreddits})
|
|
||||||
}
|
|
||||||
defer stop()
|
|
||||||
|
|
||||||
// This time, let's block so the bot will announce (ideally) forever.
|
|
||||||
if err := wait(); err != nil {
|
|
||||||
ln.Fatal(ln.F{"err": err, "action": "reddit_wait"})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type announcer struct {
|
|
||||||
cfg *config
|
|
||||||
tg *tgbotapi.BotAPI
|
|
||||||
md *madon.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *announcer) Post(post *reddit.Post) error {
|
|
||||||
txt := fmt.Sprintf("me irl\n%s\n(https://reddit.com%s by /u/%s)", post.URL, post.Permalink, post.Author)
|
|
||||||
|
|
||||||
msg := tgbotapi.NewMessage(a.cfg.TelegramChannelID, txt)
|
|
||||||
tmsg, err := a.tg.Send(msg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
toot, err := a.md.PostStatus(txt, 0, nil, false, "", "public")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ln.Log(ln.F{"action": "new_post", "url": post.URL, "permalink": post.Permalink, "redditor": post.Author, "toot_id": toot.ID, "tg_id": tmsg.MessageID})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"image"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Xe/uuid"
|
|
||||||
"github.com/fogleman/primitive/primitive"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
|
||||||
"gopkg.in/telegram-bot-api.v4"
|
|
||||||
|
|
||||||
// image formats
|
|
||||||
_ "image/jpeg"
|
|
||||||
_ "image/png"
|
|
||||||
|
|
||||||
_ "github.com/hullerob/go.farbfeld"
|
|
||||||
_ "golang.org/x/image/bmp"
|
|
||||||
_ "golang.org/x/image/tiff"
|
|
||||||
_ "golang.org/x/image/webp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
bot, err := tgbotapi.NewBotAPI(os.Getenv("TELEGRAM_TOKEN"))
|
|
||||||
if err != nil {
|
|
||||||
log.Panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ps, ok := puushLogin(os.Getenv("PUUSH_KEY"))
|
|
||||||
if !ok {
|
|
||||||
log.Fatal("puush login failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
u := tgbotapi.NewUpdate(0)
|
|
||||||
u.Timeout = 60
|
|
||||||
|
|
||||||
updates, err := bot.GetUpdatesChan(u)
|
|
||||||
|
|
||||||
for update := range updates {
|
|
||||||
if update.Message == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err := renderImg(bot, ps, update)
|
|
||||||
if err != nil {
|
|
||||||
msg := tgbotapi.NewMessage(update.Message.Chat.ID, "error: "+err.Error())
|
|
||||||
log.Printf("error in processing message from %s: %v", update.Message.From.String(), err)
|
|
||||||
bot.Send(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func stepImg(img image.Image, count int) image.Image {
|
|
||||||
bg := primitive.MakeColor(primitive.AverageImageColor(img))
|
|
||||||
model := primitive.NewModel(img, bg, 512, runtime.NumCPU())
|
|
||||||
|
|
||||||
for range make([]struct{}, count) {
|
|
||||||
model.Step(primitive.ShapeTypeTriangle, 128, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return model.Context.Image()
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderImg(bot *tgbotapi.BotAPI, ps string, update tgbotapi.Update) error {
|
|
||||||
msg := update.Message
|
|
||||||
|
|
||||||
// ignore chats without photos
|
|
||||||
if len(*msg.Photo) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
p := *msg.Photo
|
|
||||||
pho := p[len(p)-1]
|
|
||||||
fu, err := bot.GetFileDirectURL(pho.FileID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.Get(fu)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
img, ifmt, err := image.Decode(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("%s: image id %s loaded (%s)", msg.From, pho.FileID, ifmt)
|
|
||||||
umsg := tgbotapi.NewMessage(update.Message.Chat.ID, "rendering... (may take a while)")
|
|
||||||
bot.Send(umsg)
|
|
||||||
|
|
||||||
before := time.Now()
|
|
||||||
imgs := []image.Image{}
|
|
||||||
|
|
||||||
for i := range make([]struct{}, 10) {
|
|
||||||
log.Printf("%s: starting frame render", msg.From)
|
|
||||||
imgs = append(imgs, stepImg(img, 150))
|
|
||||||
log.Printf("%s: frame rendered", msg.From)
|
|
||||||
|
|
||||||
umsg = tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("frame %d/10 rendered", i+1))
|
|
||||||
bot.Send(umsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
gpath := "./var/" + update.Message.From.String() + ".gif"
|
|
||||||
err = primitive.SaveGIFImageMagick(gpath, imgs, 15, 15)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
after := time.Now().Sub(before)
|
|
||||||
|
|
||||||
buf, err := os.Open(gpath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer os.Remove(gpath)
|
|
||||||
|
|
||||||
umsg = tgbotapi.NewMessage(update.Message.Chat.ID, "uploading (took "+after.String()+" to render)")
|
|
||||||
bot.Send(umsg)
|
|
||||||
|
|
||||||
furl, err := puush(ps, uuid.New()+".gif", buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
omsg := tgbotapi.NewMessage(update.Message.Chat.ID, furl.String())
|
|
||||||
_, err = bot.Send(omsg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,93 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Puush constants
|
|
||||||
const (
|
|
||||||
PuushBase = "https://puush.me/api/"
|
|
||||||
PuushAuthURL = "https://puush.me/api/auth/"
|
|
||||||
PuushUploadURL = "https://puush.me/api/up/"
|
|
||||||
)
|
|
||||||
|
|
||||||
func puushLogin(key string) (string, bool) {
|
|
||||||
r, err := http.PostForm(PuushAuthURL, url.Values{"k": {key}})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
body, _ := ioutil.ReadAll(r.Body)
|
|
||||||
r.Body.Close()
|
|
||||||
info := strings.Split(string(body), ",")
|
|
||||||
if info[0] == "-1" {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
session := info[1]
|
|
||||||
return session, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func puush(session, fname string, fin io.Reader) (*url.URL, error) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
w := multipart.NewWriter(buf)
|
|
||||||
kwriter, err := w.CreateFormField("k")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
io.WriteString(kwriter, session)
|
|
||||||
|
|
||||||
file, _ := ioutil.ReadAll(fin)
|
|
||||||
|
|
||||||
h := md5.New()
|
|
||||||
h.Write(file)
|
|
||||||
|
|
||||||
cwriter, err := w.CreateFormField("c")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
io.WriteString(cwriter, fmt.Sprintf("%x", h.Sum(nil)))
|
|
||||||
|
|
||||||
zwriter, err := w.CreateFormField("z")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
io.WriteString(zwriter, "poop") // They must think their protocol is shit
|
|
||||||
|
|
||||||
fwriter, err := w.CreateFormFile("f", fname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fwriter.Write(file)
|
|
||||||
|
|
||||||
w.Close()
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "http://puush.me/api/up", buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", w.FormDataContentType())
|
|
||||||
client := &http.Client{}
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
body, _ := ioutil.ReadAll(res.Body)
|
|
||||||
res.Body.Close()
|
|
||||||
info := strings.Split(string(body), ",")
|
|
||||||
if info[0] == "0" {
|
|
||||||
return url.Parse(info[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("upload failed")
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
62b230097e9c9534ca2074782b25d738c4b68964 (dirty) github.com/Xe/uuid
|
|
||||||
ee8994ff90057955c428a5a949da5d064bf3ce6b github.com/fogleman/gg
|
|
||||||
80f39ceaa8f4c66acb28aba6abe6b15128c06113 github.com/fogleman/primitive/primitive
|
|
||||||
bcfeb16b74e8aea9e2fe043406f2ef74b1cb0759 github.com/golang/freetype/raster
|
|
||||||
bcfeb16b74e8aea9e2fe043406f2ef74b1cb0759 github.com/golang/freetype/truetype
|
|
||||||
b572f0728b691aae4256edb2e408279146eafe52 github.com/hullerob/go.farbfeld
|
|
||||||
325433c502d409f3c3dc820098fb0cfe38d98dc7 github.com/joho/godotenv
|
|
||||||
325433c502d409f3c3dc820098fb0cfe38d98dc7 github.com/joho/godotenv/autoload
|
|
||||||
a90a01d73ae432e2611d178c18367fbaa13e0154 github.com/technoweenie/multipartstreamer
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/bmp
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/draw
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/font
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/font/basicfont
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/math/f64
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/math/fixed
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/riff
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/tiff
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/tiff/lzw
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/vp8
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/vp8l
|
|
||||||
426cfd8eeb6e08ab1932954e09e3c2cb2bc6e36d golang.org/x/image/webp
|
|
||||||
0a57807db79efce7f6719fbb2c0e0f83fda79aec (dirty) gopkg.in/telegram-bot-api.v4
|
|
|
@ -1 +0,0 @@
|
||||||
atheme
|
|
|
@ -1,95 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/Xe/Tetra/atheme"
|
|
||||||
"github.com/howeyc/gopass"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
client *atheme.Atheme
|
|
||||||
serverString = flag.String("server", "http://127.0.0.1:8080/xmlrpc", "what http://server:port to connect to")
|
|
||||||
cookie = flag.String("cookie", "", "authentication cookie to use")
|
|
||||||
user = flag.String("user", "", "username to use")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
command := flag.Arg(0)
|
|
||||||
if command == "" {
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
var rest []string
|
|
||||||
|
|
||||||
if n := flag.NArg(); n > 1 {
|
|
||||||
rest = flag.Args()[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = rest
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
client, err = atheme.NewAtheme(*serverString)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch command {
|
|
||||||
case "login":
|
|
||||||
var username string
|
|
||||||
var password string
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
|
|
||||||
fmt.Print("Username: ")
|
|
||||||
|
|
||||||
for {
|
|
||||||
scanner.Scan()
|
|
||||||
username = scanner.Text()
|
|
||||||
|
|
||||||
if scanner.Err() == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print("Username: ")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print("Password: ")
|
|
||||||
for {
|
|
||||||
password = string(gopass.GetPasswdMasked())
|
|
||||||
if password != "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fmt.Print("Password: ")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = client.Login(username, password)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Authcookie: %s\n", client.Authcookie)
|
|
||||||
|
|
||||||
case "command":
|
|
||||||
if *cookie == "" {
|
|
||||||
log.Fatal("specify cookie")
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Authcookie = *cookie
|
|
||||||
client.Account = *user
|
|
||||||
|
|
||||||
output, err := client.Command(rest...)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(output)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
dbupload
|
|
|
@ -1,49 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
type UploadImage struct {
|
|
||||||
Image struct {
|
|
||||||
SourceURL string `json:"source_url"`
|
|
||||||
Tags string `json:"tag_list"`
|
|
||||||
ImageURL string `json:"image_url"`
|
|
||||||
} `json:"image"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Image struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
IDNumber int `json:"id_number"`
|
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
DuplicateReports []interface{} `json:"duplicate_reports"`
|
|
||||||
FileName string `json:"file_name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Uploader string `json:"uploader"`
|
|
||||||
Image string `json:"image"`
|
|
||||||
Score int `json:"score"`
|
|
||||||
Upvotes int `json:"upvotes"`
|
|
||||||
Downvotes int `json:"downvotes"`
|
|
||||||
Faves int `json:"faves"`
|
|
||||||
CommentCount int `json:"comment_count"`
|
|
||||||
Tags string `json:"tags"`
|
|
||||||
TagIds []string `json:"tag_ids"`
|
|
||||||
Width int `json:"width"`
|
|
||||||
Height int `json:"height"`
|
|
||||||
AspectRatio float64 `json:"aspect_ratio"`
|
|
||||||
OriginalFormat string `json:"original_format"`
|
|
||||||
MimeType string `json:"mime_type"`
|
|
||||||
Sha512Hash string `json:"sha512_hash"`
|
|
||||||
OrigSha512Hash string `json:"orig_sha512_hash"`
|
|
||||||
SourceURL string `json:"source_url"`
|
|
||||||
License string `json:"license"`
|
|
||||||
Representations struct {
|
|
||||||
ThumbTiny string `json:"thumb_tiny"`
|
|
||||||
ThumbSmall string `json:"thumb_small"`
|
|
||||||
Thumb string `json:"thumb"`
|
|
||||||
Small string `json:"small"`
|
|
||||||
Medium string `json:"medium"`
|
|
||||||
Large string `json:"large"`
|
|
||||||
Tall string `json:"tall"`
|
|
||||||
Full string `json:"full"`
|
|
||||||
} `json:"representations"`
|
|
||||||
IsRendered bool `json:"is_rendered"`
|
|
||||||
IsOptimized bool `json:"is_optimized"`
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
dbmanifestgen
|
|
|
@ -1 +0,0 @@
|
||||||
../db.go
|
|
|
@ -1,62 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
serverloc = flag.String("serverloc", "http://static.xeserv.us/", "server to prepend to url paths")
|
|
||||||
sourceloc = flag.String("sourceloc", "", "source URL for metadata generation")
|
|
||||||
set = flag.String("set", "", "comic set")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if flag.NArg() == 0 {
|
|
||||||
fmt.Printf("%s: <dir>\n", os.Args[0])
|
|
||||||
flag.Usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
if *sourceloc == "" {
|
|
||||||
log.Fatal("Need a source location")
|
|
||||||
}
|
|
||||||
|
|
||||||
images, err := ioutil.ReadDir(flag.Arg(0))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, image := range images {
|
|
||||||
if strings.HasSuffix(image.Name(), ".json") {
|
|
||||||
log.Printf("Skipped %s...", image)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fout, err := os.Create(image.Name() + ".json")
|
|
||||||
|
|
||||||
var i UploadImage
|
|
||||||
i.Image.SourceURL = *sourceloc
|
|
||||||
i.Image.ImageURL = *serverloc + filepath.Base(image.Name())
|
|
||||||
i.Image.Tags = "explicit"
|
|
||||||
|
|
||||||
if *set != "" {
|
|
||||||
i.Image.Tags = i.Image.Tags + ", comic:" + *set
|
|
||||||
}
|
|
||||||
|
|
||||||
outdata, err := json.MarshalIndent(&i, "", "\t")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fout.Write(outdata)
|
|
||||||
fout.Close()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
cat << EOF
|
|
||||||
{
|
|
||||||
"image": {
|
|
||||||
"source_url": "$1",
|
|
||||||
"tag_list": "$2",
|
|
||||||
"image_url": "$3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOF
|
|
|
@ -1,116 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
apiKeyLocation = flag.String("apikeyloc", "/home/xena/.local/share/within/db.key", "Derpibooru API key location")
|
|
||||||
cookieLocation = flag.String("cookieloc", "/home/xena/.db.cookie", "Location for magic cookie")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if flag.NArg() == 0 {
|
|
||||||
fmt.Printf("%s: <image to upload>\n", os.Args[0])
|
|
||||||
fmt.Printf("All files must have a manifest json file.\n")
|
|
||||||
flag.Usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
dbkey, err := ioutil.ReadFile(*apiKeyLocation)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mydbkey := strings.Split(string(dbkey), "\n")[0]
|
|
||||||
|
|
||||||
cookie, err := ioutil.ReadFile(*cookieLocation)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
image := flag.Arg(0)
|
|
||||||
|
|
||||||
if strings.HasSuffix(image, ".json") {
|
|
||||||
log.Printf("Skipped %s...", image)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
metafin, err := os.Open(image + ".json")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("image " + image + " MUST have description manifest for derpibooru")
|
|
||||||
}
|
|
||||||
defer metafin.Close()
|
|
||||||
|
|
||||||
metabytes, err := ioutil.ReadAll(metafin)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var meta UploadImage
|
|
||||||
err = json.Unmarshal(metabytes, &meta)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
imfin, err := os.Open(image)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("cannot open " + image)
|
|
||||||
}
|
|
||||||
defer imfin.Close()
|
|
||||||
|
|
||||||
if meta.Image.ImageURL == "" {
|
|
||||||
panic("need file uploaded somewhere?")
|
|
||||||
}
|
|
||||||
|
|
||||||
outmetabytes, err := json.Marshal(&meta)
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "https://derpibooru.org/images.json?key="+mydbkey, bytes.NewBuffer(outmetabytes))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &http.Client{}
|
|
||||||
|
|
||||||
req.Header = http.Header{
|
|
||||||
"User-Agent": {"Xena's crappy upload tool"},
|
|
||||||
"Cookie": {string(cookie)},
|
|
||||||
"Content-Type": {"application/json"},
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("%#v", err)
|
|
||||||
fmt.Printf("Request ID: %s\n", resp.Header.Get("X-Request-Id"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != 201 {
|
|
||||||
io.Copy(os.Stdout, resp.Body)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
respbytes, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var i Image
|
|
||||||
json.Unmarshal(respbytes, &i)
|
|
||||||
|
|
||||||
fmt.Printf("Uploaded as https://derpibooru.org/%d\n", i.IDNumber)
|
|
||||||
|
|
||||||
time.Sleep(20 * time.Second)
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"log"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
|
||||||
"github.com/namsral/flag"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
cfg = flag.String("config", "/home/xena/.local/share/within/discordaway.cfg", "configuration file")
|
|
||||||
username = flag.String("username", "", "Discord username to use")
|
|
||||||
password = flag.String("password", "", "Discord password to use")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
dg, err := discordgo.New(*username, *password)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dg.AddHandler(messageCreate)
|
|
||||||
|
|
||||||
err = dg.Open()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("monitoring tmux status...")
|
|
||||||
t := time.NewTicker(300 * time.Second)
|
|
||||||
|
|
||||||
ok, err := isTmuxAttached()
|
|
||||||
log.Println(ok, err)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-t.C:
|
|
||||||
at, err := isTmuxAttached()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if at {
|
|
||||||
log.Println("Cadey is away, marking as away on Discord")
|
|
||||||
dg.UpdateStatus(600, "around with reality for some reason")
|
|
||||||
} else {
|
|
||||||
log.Println("Cadey is back!!!")
|
|
||||||
dg.UpdateStatus(0, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isTmuxAttached() (bool, error) {
|
|
||||||
cmd := exec.Command("/usr/bin/tmux", "ls", "-F", "#{?session_attached,attached,not attached}")
|
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes.HasPrefix(output, []byte("attached")), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
||||||
if m.Author.ID == "72838115944828928" {
|
|
||||||
content := m.ContentWithMentionsReplaced()
|
|
||||||
|
|
||||||
if strings.HasPrefix(content, "TODO: ") {
|
|
||||||
todoBody := strings.SplitN(content, "TODO: ", 2)[1]
|
|
||||||
|
|
||||||
log.Printf("todo added: %s", todoBody)
|
|
||||||
todoFields := strings.Fields(todoBody)
|
|
||||||
|
|
||||||
cmd := exec.Command("/home/xena/go/bin/todo", append([]string{"add"}, todoFields...)...)
|
|
||||||
err := cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cmd.Wait()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
dokku
|
|
|
@ -1,55 +0,0 @@
|
||||||
Dokku
|
|
||||||
=====
|
|
||||||
|
|
||||||
This is a simple command line tool to interface with Dokku servers. This is
|
|
||||||
a port of my shell extension
|
|
||||||
[`dokku.zsh`](https://github.com/Xe/dotfiles/blob/master/.zsh/dokku.zsh) to
|
|
||||||
a nice Go binary.
|
|
||||||
|
|
||||||
This takes a configuration file for defining multiple servers:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[server "default"]
|
|
||||||
user = dokku
|
|
||||||
host = panel.apps.xeserv.us
|
|
||||||
sshkey = /.ssh/id_rsa
|
|
||||||
```
|
|
||||||
|
|
||||||
By default it will imply that the SSH key is `~/.ssh/id_rsa` and that the
|
|
||||||
username is `dokku`. By default the server named `default` will be used for
|
|
||||||
command execution.
|
|
||||||
|
|
||||||
TODO
|
|
||||||
----
|
|
||||||
|
|
||||||
- [ ] Allow interactive commands
|
|
||||||
- [ ] Directly pipe stdin and stdout to the ssh connection
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
```
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
```
|
|
|
@ -1,11 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Server map[string]*Server
|
|
||||||
}
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
SSHKey string // if blank default key will be used.
|
|
||||||
Host string // hostname of the dokku server
|
|
||||||
User string // if blank username will be dokku
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
[server "default"]
|
|
||||||
host = panel.apps.xeserv.us
|
|
|
@ -1,61 +0,0 @@
|
||||||
package main // christine.website/go/tools/dokku
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.google.com/p/gcfg"
|
|
||||||
"github.com/hypersleep/easyssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
cfgPath = flag.String("cfg", "", "configuration path, default is ~/.dokku.cfg")
|
|
||||||
serverName = flag.String("server", "default", "server to use out of dokku config")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *cfgPath == "" {
|
|
||||||
*cfgPath = os.Getenv("HOME") + "/.dokku.cfg"
|
|
||||||
}
|
|
||||||
|
|
||||||
var cfg Config
|
|
||||||
err := gcfg.ReadFileInto(&cfg, *cfgPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var server *Server
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if server, ok = cfg.Server[*serverName]; !ok {
|
|
||||||
log.Fatalf("server %s not defined in configuration file %s", *serverName, *cfgPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if server.User == "" {
|
|
||||||
server.User = "dokku"
|
|
||||||
}
|
|
||||||
|
|
||||||
if server.SSHKey == "" {
|
|
||||||
server.SSHKey = "/.ssh/id_rsa"
|
|
||||||
}
|
|
||||||
|
|
||||||
ssh := &easyssh.MakeConfig{
|
|
||||||
User: server.User,
|
|
||||||
Server: server.Host,
|
|
||||||
Key: server.SSHKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
command := strings.Join(flag.Args(), " ")
|
|
||||||
|
|
||||||
res, err := ssh.Run(command)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print(res)
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
ghstat
|
|
|
@ -1,9 +0,0 @@
|
||||||
# ghstat
|
|
||||||
--
|
|
||||||
Command ghstat shows the status of GitHub via their status API.
|
|
||||||
|
|
||||||
Usage of ./ghstat:
|
|
||||||
-message=false: show last message?
|
|
||||||
|
|
||||||
This follows https://status.github.com/api for all but the list of all recent
|
|
||||||
status messages.
|
|
|
@ -1,10 +0,0 @@
|
||||||
/*
|
|
||||||
Command ghstat shows the status of GitHub via their status API.
|
|
||||||
|
|
||||||
Usage of ./ghstat:
|
|
||||||
-message=false: show last message?
|
|
||||||
|
|
||||||
This follows https://status.github.com/api for all but the list of all
|
|
||||||
recent status messages.
|
|
||||||
*/
|
|
||||||
package main
|
|
|
@ -1,54 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
messageFlag = flag.Bool("message", false, "show last message?")
|
|
||||||
|
|
||||||
// TODO: implement
|
|
||||||
//shellFlag = flag.Bool("s", false, "show as shell prompt artifact")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *messageFlag {
|
|
||||||
m, err := getMessage()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Last message:\n")
|
|
||||||
fmt.Printf("Status: %s\n", m.Status)
|
|
||||||
fmt.Printf("Message: %s\n", m.Body)
|
|
||||||
|
|
||||||
t, err := time.Parse(time.RFC3339, m.CreatedOn)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Time: %s\n", t.Format(time.ANSIC))
|
|
||||||
} else {
|
|
||||||
s, err := getStatus()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := time.Parse(time.RFC3339, s.LastUpdated)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Status: %s (%s)\n", s.Status, t.Format(time.ANSIC))
|
|
||||||
|
|
||||||
if s.Status != "good" {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Status struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
LastUpdated string `json:"last_updated"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
Body string `json:"body"`
|
|
||||||
CreatedOn string `json:"created_on"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMessage() (Message, error) {
|
|
||||||
m := Message{}
|
|
||||||
|
|
||||||
resp, err := http.Get("https://status.github.com/api/last-message.json")
|
|
||||||
if err != nil {
|
|
||||||
return Message{}, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
content, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return Message{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(content, &m)
|
|
||||||
if err != nil {
|
|
||||||
return Message{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStatus() (Status, error) {
|
|
||||||
s := Status{}
|
|
||||||
|
|
||||||
resp, err := http.Get("https://status.github.com/api/status.json")
|
|
||||||
if err != nil {
|
|
||||||
return Status{}, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
content, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return Status{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(content, &s)
|
|
||||||
if err != nil {
|
|
||||||
return Status{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
|
@ -1,661 +0,0 @@
|
||||||
## `json`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local json = require "json"
|
|
||||||
```
|
|
||||||
|
|
||||||
Json encoder/decoder
|
|
||||||
|
|
||||||
The following functions are exposed by the library:
|
|
||||||
|
|
||||||
decode(string): Decodes a JSON string. Returns nil and an error string if
|
|
||||||
the string could not be decoded.
|
|
||||||
encode(value): Encodes a value into a JSON string. Returns nil and an error
|
|
||||||
string if the value could not be encoded.
|
|
||||||
|
|
||||||
## `xmlpath`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local xmlpath = require "xmlpath"
|
|
||||||
```
|
|
||||||
|
|
||||||
XMLPath style iteration
|
|
||||||
|
|
||||||
xml ="<bookist><book>x1</book><book>x2</book><book>x3</book></booklist>"
|
|
||||||
local xmlpath = require("xmlpath")
|
|
||||||
node,err = xmlpath.loadxml(xml)
|
|
||||||
path,err = xmlpath.compile("//book")
|
|
||||||
|
|
||||||
it = path:iter(node)
|
|
||||||
for k,v in pairs(it) do
|
|
||||||
print(k,v:string())
|
|
||||||
end
|
|
||||||
|
|
||||||
## `http`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local http = require("http")
|
|
||||||
```
|
|
||||||
|
|
||||||
HTTP client library
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
- [`http.delete(url [, options])`](#httpdeleteurl--options)
|
|
||||||
- [`http.get(url [, options])`](#httpgeturl--options)
|
|
||||||
- [`http.head(url [, options])`](#httpheadurl--options)
|
|
||||||
- [`http.patch(url [, options])`](#httppatchurl--options)
|
|
||||||
- [`http.post(url [, options])`](#httpposturl--options)
|
|
||||||
- [`http.put(url [, options])`](#httpputurl--options)
|
|
||||||
- [`http.request(method, url [, options])`](#httprequestmethod-url--options)
|
|
||||||
- [`http.request_batch(requests)`](#httprequest_batchrequests)
|
|
||||||
- [`http.response`](#httpresponse)
|
|
||||||
|
|
||||||
#### http.delete(url [, options])
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| url | String | URL of the resource to load |
|
|
||||||
| options | Table | Additional options |
|
|
||||||
|
|
||||||
**Options**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| query | String | URL encoded query params |
|
|
||||||
| cookies | Table | Additional cookies to send with the request |
|
|
||||||
| headers | Table | Additional headers to send with the request |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
[http.response](#httpresponse) or (nil, error message)
|
|
||||||
|
|
||||||
#### http.get(url [, options])
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| url | String | URL of the resource to load |
|
|
||||||
| options | Table | Additional options |
|
|
||||||
|
|
||||||
**Options**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| query | String | URL encoded query params |
|
|
||||||
| cookies | Table | Additional cookies to send with the request |
|
|
||||||
| headers | Table | Additional headers to send with the request |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
[http.response](#httpresponse) or (nil, error message)
|
|
||||||
|
|
||||||
#### http.head(url [, options])
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| url | String | URL of the resource to load |
|
|
||||||
| options | Table | Additional options |
|
|
||||||
|
|
||||||
**Options**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| query | String | URL encoded query params |
|
|
||||||
| cookies | Table | Additional cookies to send with the request |
|
|
||||||
| headers | Table | Additional headers to send with the request |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
[http.response](#httpresponse) or (nil, error message)
|
|
||||||
|
|
||||||
#### http.patch(url [, options])
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| url | String | URL of the resource to load |
|
|
||||||
| options | Table | Additional options |
|
|
||||||
|
|
||||||
**Options**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| query | String | URL encoded query params |
|
|
||||||
| cookies | Table | Additional cookies to send with the request |
|
|
||||||
| body | String | Request body. |
|
|
||||||
| form | String | Deprecated. URL encoded request body. This will also set the `Content-Type` header to `application/x-www-form-urlencoded` |
|
|
||||||
| headers | Table | Additional headers to send with the request |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
[http.response](#httpresponse) or (nil, error message)
|
|
||||||
|
|
||||||
#### http.post(url [, options])
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| url | String | URL of the resource to load |
|
|
||||||
| options | Table | Additional options |
|
|
||||||
|
|
||||||
**Options**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| query | String | URL encoded query params |
|
|
||||||
| cookies | Table | Additional cookies to send with the request |
|
|
||||||
| body | String | Request body. |
|
|
||||||
| form | String | Deprecated. URL encoded request body. This will also set the `Content-Type` header to `application/x-www-form-urlencoded` |
|
|
||||||
| headers | Table | Additional headers to send with the request |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
[http.response](#httpresponse) or (nil, error message)
|
|
||||||
|
|
||||||
#### http.put(url [, options])
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| url | String | URL of the resource to load |
|
|
||||||
| options | Table | Additional options |
|
|
||||||
|
|
||||||
**Options**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| query | String | URL encoded query params |
|
|
||||||
| cookies | Table | Additional cookies to send with the request |
|
|
||||||
| body | String | Request body. |
|
|
||||||
| form | String | Deprecated. URL encoded request body. This will also set the `Content-Type` header to `application/x-www-form-urlencoded` |
|
|
||||||
| headers | Table | Additional headers to send with the request |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
[http.response](#httpresponse) or (nil, error message)
|
|
||||||
|
|
||||||
#### http.request(method, url [, options])
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| method | String | The HTTP request method |
|
|
||||||
| url | String | URL of the resource to load |
|
|
||||||
| options | Table | Additional options |
|
|
||||||
|
|
||||||
**Options**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| query | String | URL encoded query params |
|
|
||||||
| cookies | Table | Additional cookies to send with the request |
|
|
||||||
| body | String | Request body. |
|
|
||||||
| form | String | Deprecated. URL encoded request body. This will also set the `Content-Type` header to `application/x-www-form-urlencoded` |
|
|
||||||
| headers | Table | Additional headers to send with the request |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
[http.response](#httpresponse) or (nil, error message)
|
|
||||||
|
|
||||||
#### http.request_batch(requests)
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| -------- | ----- | ----------- |
|
|
||||||
| requests | Table | A table of requests to send. Each request item is by itself a table containing [http.request](#httprequestmethod-url--options) parameters for the request |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
[[http.response](#httpresponse)] or ([[http.response](#httpresponse)], [error message])
|
|
||||||
|
|
||||||
#### http.response
|
|
||||||
|
|
||||||
The `http.response` table contains information about a completed HTTP request.
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ----------- | ------ | ----------- |
|
|
||||||
| body | String | The HTTP response body |
|
|
||||||
| body_size | Number | The size of the HTTP reponse body in bytes |
|
|
||||||
| headers | Table | The HTTP response headers |
|
|
||||||
| cookies | Table | The cookies sent by the server in the HTTP response |
|
|
||||||
| status_code | Number | The HTTP response status code |
|
|
||||||
| url | String | The final URL the request ended pointing to after redirects |
|
|
||||||
|
|
||||||
## `url`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local url = require "url"
|
|
||||||
```
|
|
||||||
|
|
||||||
URL parsing library
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
- [`url.parse(url)`](#urlparseurl)
|
|
||||||
- [`url.build(options)`](#urlbuildoptions)
|
|
||||||
- [`url.build_query_string(query_params)`](#urlbuild_query_stringquery_params)
|
|
||||||
- [`url.resolve(from, to)`](#urlresolvefrom-to)
|
|
||||||
|
|
||||||
#### url.parse(url)
|
|
||||||
|
|
||||||
Parse URL into a table of key/value components.
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ------ | ----------- |
|
|
||||||
| url | String | URL to parsed |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
Table with parsed URL or (nil, error message)
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| -------- | ------ | ----------- |
|
|
||||||
| scheme | String | Scheme of the URL |
|
|
||||||
| username | String | Username |
|
|
||||||
| password | String | Password |
|
|
||||||
| host | String | Host and port of the URL |
|
|
||||||
| path | String | Path |
|
|
||||||
| query | String | Query string |
|
|
||||||
| fragment | String | Fragment |
|
|
||||||
|
|
||||||
#### url.build(options)
|
|
||||||
|
|
||||||
Assemble a URL string from a table of URL components.
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------- | ----- | ----------- |
|
|
||||||
| options | Table | Table with URL components, see [`url.parse`](#urlparseurl) for list of valid components |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
String
|
|
||||||
|
|
||||||
#### url.build_query_string(query_params)
|
|
||||||
|
|
||||||
Assemble table of query string parameters into a string.
|
|
||||||
|
|
||||||
**Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------------ | ----- | ----------- |
|
|
||||||
| query_params | Table | Table with query parameters |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
String
|
|
||||||
|
|
||||||
#### url.resolve(from, to)
|
|
||||||
|
|
||||||
Take a base URL, and a href URL, and resolve them as a browser would for an anchor tag.
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ---- | ------ | ----------- |
|
|
||||||
| from | String | base URL |
|
|
||||||
| to | String | href URL |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
String or (nil, error message)
|
|
||||||
|
|
||||||
## `env`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local env = require "env"
|
|
||||||
```
|
|
||||||
|
|
||||||
Environment manipulation
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
#### `env.set(key, value)`
|
|
||||||
|
|
||||||
Same `os.setenv`
|
|
||||||
|
|
||||||
#### `env.get(key)`
|
|
||||||
|
|
||||||
Same `os.getenv`
|
|
||||||
|
|
||||||
#### `env.loadfile(file)`
|
|
||||||
|
|
||||||
Loads environment variables from a file. The file is as the following:
|
|
||||||
|
|
||||||
```
|
|
||||||
AAA=BBB
|
|
||||||
CCC=DDD
|
|
||||||
```
|
|
||||||
|
|
||||||
If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
## `fs`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local fs = require "fs"
|
|
||||||
```
|
|
||||||
|
|
||||||
Filesystem manipulation
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
#### `fs.exists(file)`
|
|
||||||
|
|
||||||
Returns true if the file exists.
|
|
||||||
|
|
||||||
#### `fs.read(file)`
|
|
||||||
|
|
||||||
Reads file content and return it. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.write(file, content, [mode])`
|
|
||||||
|
|
||||||
Writes content to the file. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.mkdir(path, [mode, recursive])`
|
|
||||||
|
|
||||||
Create directory. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.remove(path, [recursive])`
|
|
||||||
|
|
||||||
Remove path. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.symlink(target, link)`
|
|
||||||
|
|
||||||
Create symbolic link. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.dirname(path)`
|
|
||||||
|
|
||||||
Returns all but the last element of path.
|
|
||||||
|
|
||||||
#### `fs.basename(path)`
|
|
||||||
|
|
||||||
Returns the last element of path.
|
|
||||||
|
|
||||||
#### `fs.realpath(path)`
|
|
||||||
|
|
||||||
Returns the real path of a given path in the os. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.getcwd()`
|
|
||||||
|
|
||||||
Returns the current working directory. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.chdir(path)`
|
|
||||||
|
|
||||||
Changes the current working directory. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.file()`
|
|
||||||
|
|
||||||
Returns the script file path. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.dir()`
|
|
||||||
|
|
||||||
Returns the directory path that is parent of the script file. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `fs.glob(pattern, function)`
|
|
||||||
|
|
||||||
Run the callback function with the files matching pattern. See below example:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local fs = require("fs")
|
|
||||||
local ret, err = fs.glob("/tmp/*", function(file)
|
|
||||||
print(file.path)
|
|
||||||
print(file.realpath)
|
|
||||||
end)
|
|
||||||
```
|
|
||||||
|
|
||||||
## `markdown`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local markdown = require "markdown"
|
|
||||||
```
|
|
||||||
|
|
||||||
Markdown -> HTML for string and file
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
#### `markdown.dostring(text)`
|
|
||||||
|
|
||||||
Returns HTML string generated from the markdown text.
|
|
||||||
|
|
||||||
#### `markdown.dofile(file)`
|
|
||||||
|
|
||||||
Returns HTML string generated from the markdown text file. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
## `question`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local question = require "question"
|
|
||||||
```
|
|
||||||
|
|
||||||
Prompt library
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
* `question.ask(text)`
|
|
||||||
* `question.secret(text)`
|
|
||||||
|
|
||||||
## `ssh`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local ssh = require "ssh"
|
|
||||||
```
|
|
||||||
|
|
||||||
SSH client library
|
|
||||||
|
|
||||||
https://github.com/kohkimakimoto/gluassh/blob/master/gluassh_test.go
|
|
||||||
|
|
||||||
## `template`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local template = require "template"
|
|
||||||
```
|
|
||||||
|
|
||||||
Go text templates
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
#### `template.dostring(text, table)`
|
|
||||||
|
|
||||||
Returns string generated by text template with the table values. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
#### `template.dofile(file, table)`
|
|
||||||
|
|
||||||
Returns string generated by file template with the table values. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
## `yaml`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local yaml = require "yaml"
|
|
||||||
```
|
|
||||||
|
|
||||||
Yaml -> table parser
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
#### `yaml.parse(string)`
|
|
||||||
|
|
||||||
Parses yaml formatted string and returns a table. If this function fails, it returns `nil`, plus a string describing the error.
|
|
||||||
|
|
||||||
## `flag`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local flag = require "flag"
|
|
||||||
```
|
|
||||||
|
|
||||||
Command line flag parsing.
|
|
||||||
|
|
||||||
See the tests here: https://github.com/otm/gluaflag
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local flag = require "flag"
|
|
||||||
|
|
||||||
fs = flag.new()
|
|
||||||
|
|
||||||
fs:string("name", "foo", "String help string")
|
|
||||||
fs:intArg("title", 1, "Title")
|
|
||||||
fs:numberArg("title", 1, "Title")
|
|
||||||
|
|
||||||
flags = fs:parse(arg) -- arg is the remaining command line arguments
|
|
||||||
assert(flags.title == 2, "expected title to be 2")
|
|
||||||
assert(flags.title == 2.32, "expected title to be 2.32")
|
|
||||||
```
|
|
||||||
|
|
||||||
## `sh`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local sh = require "sh"
|
|
||||||
```
|
|
||||||
|
|
||||||
gluash is a interface to call any program as it were a function. Programs are executed asynchronously to enable streaming of data in pipes.
|
|
||||||
|
|
||||||
In all discussions bellow the imported module will be referred to as `sh`.
|
|
||||||
|
|
||||||
Commands are called just like functions, executed on the sh module.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
sh.ls("/")
|
|
||||||
```
|
|
||||||
|
|
||||||
For commands that have exotic names, names that are reserved words, or to execute absolute or relative paths call the sh module directly.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
sh("/bin/ls", "/")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Multiple Arguments
|
|
||||||
Commands with multiple arguments have to be invoked with a separate string for each argument.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- this works
|
|
||||||
sh.ls("-la", "/")
|
|
||||||
|
|
||||||
-- this does not work
|
|
||||||
sh.ls("-la /")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Piping
|
|
||||||
Piping in sh is done almost like piping in the shell. Just call next command as a method on the previous command.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
sh.du("-sb"):sort("-rn"):print()
|
|
||||||
```
|
|
||||||
|
|
||||||
If the command has a exotic name, or a reserved word, call the command through `cmd(path, ...args)`. The first argument in `cmd` is the path.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
sh.du("-sb"):cmd("sort", "-rn"):print()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Waiting for Processes
|
|
||||||
All commands are executed by default in the background, so one have to explicitly wait for a process to finish. There are several ways to wait for the command to finish.
|
|
||||||
|
|
||||||
* `print()` - write stdout and stderr to stdout.
|
|
||||||
* `ok()` - aborts execution if the command's exit code is not zero
|
|
||||||
* `success()` - returns true of the commands exit code is zero
|
|
||||||
* `exitcode()` - returns the exit code of the command
|
|
||||||
|
|
||||||
### Abort by Default
|
|
||||||
It is possible to set the module to abort on errors without checking. It can be practical in some occasions, however performance will be degraded. When global exit code checks are done the commands are run in series, even in pipes, and output is saved in memory buffers.
|
|
||||||
|
|
||||||
To enable global exit code settings call the sh module with an table with the key `abort` set to true.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
sh{abort=true}
|
|
||||||
```
|
|
||||||
|
|
||||||
To read current settings in the module call the module with an empty table.
|
|
||||||
```lua
|
|
||||||
configuration = sh{}
|
|
||||||
print("abort:", configuration.abort)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Analyzing Output
|
|
||||||
There are several options to analyze the output of a command.
|
|
||||||
|
|
||||||
#### lines()
|
|
||||||
An iterator is accessible by calling the method `lines()` on the command.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
for line in sh.cat("/etc/hosts"):lines() do
|
|
||||||
print(line)
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
#### stdout([filename]), stderr([filename]), combinedOutput([filename])
|
|
||||||
`stdout()`, `stderr()`, and `combinedOutput()` all returns the output of the command as a string. An optional `filename` can be given to the method, in that case the output is also written to the file. The file will be truncated.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- print output of command
|
|
||||||
output = sh.echo("hello world"):combinedOutput("/tmp/output")
|
|
||||||
print(output)
|
|
||||||
```
|
|
||||||
|
|
||||||
In the example above will print `hello world` and it will write it to `/tmp/output`
|
|
||||||
|
|
||||||
### Glob Expansion
|
|
||||||
There is no glob expansion done on arguments, however there is a glob functionality in sh.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
sh.ls(sh.glob("*.go"))
|
|
||||||
```
|
|
||||||
|
|
||||||
## `re`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local re = require "re"
|
|
||||||
```
|
|
||||||
|
|
||||||
Regular Expressions
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
re.find , re.gsub, re.match, re.gmatch are available. These functions have the same API as Lua pattern match.
|
|
||||||
gluare uses the Go regexp package, so you can use regular expressions that are supported in the Go regexp package.
|
|
||||||
|
|
||||||
In addition, the following functions are defined:
|
|
||||||
```
|
|
||||||
gluare.quote(s string) -> string
|
|
||||||
Arguments:
|
|
||||||
|
|
||||||
s string: a string value to escape meta characters
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
string: escaped string
|
|
||||||
gluare.quote returns a string that quotes all regular expression metacharacters inside the given text.
|
|
||||||
```
|
|
||||||
|
|
||||||
## `simplebox`
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local simplebox = require "simplebox"
|
|
||||||
```
|
|
||||||
|
|
||||||
Simple encryption
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
#### Create a new instance of simplebox with a newly generated key
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local simplebox = require "simplebox"
|
|
||||||
local key = simplebox.genkey()
|
|
||||||
print("key is: " .. key)
|
|
||||||
local sb = simplebox.new()
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
|
@ -1,4 +0,0 @@
|
||||||
FROM busybox
|
|
||||||
|
|
||||||
ADD glue /glue
|
|
||||||
CMD /glue
|
|
|
@ -1,18 +0,0 @@
|
||||||
glue
|
|
||||||
====
|
|
||||||
|
|
||||||
Basically gopher-lua's cmd/glua with the following modules imported:
|
|
||||||
- https://godoc.org/layeh.com/gopher-json
|
|
||||||
- https://github.com/ailncode/gluaxmlpath
|
|
||||||
- https://github.com/cjoudrey/gluahttp
|
|
||||||
- https://github.com/cjoudrey/gluaurl
|
|
||||||
- https://github.com/kohkimakimoto/gluaenv
|
|
||||||
- https://github.com/kohkimakimoto/gluafs
|
|
||||||
- https://github.com/kohkimakimoto/gluamarkdown
|
|
||||||
- https://github.com/kohkimakimoto/gluaquestion
|
|
||||||
- https://github.com/kohkimakimoto/gluassh
|
|
||||||
- https://github.com/kohkimakimoto/gluatemplate
|
|
||||||
- https://github.com/kohkimakimoto/gluayaml
|
|
||||||
- https://github.com/otm/gluaflag
|
|
||||||
- https://github.com/otm/gluash
|
|
||||||
- https://github.com/yuin/gluare
|
|
|
@ -1,6 +0,0 @@
|
||||||
from "alpine:edge"
|
|
||||||
|
|
||||||
copy "glue", "/glue"
|
|
||||||
cmd "/glue"
|
|
||||||
flatten
|
|
||||||
tag "xena/glue"
|
|
|
@ -1,20 +0,0 @@
|
||||||
-- expects glue, $ go get -u github.com/Xe/tools/glue
|
|
||||||
local sh = require "sh"
|
|
||||||
sh { abort = true }
|
|
||||||
|
|
||||||
if os.getenv("CGO_ENABLED") ~= "0" then
|
|
||||||
error("CGO_ENABLED must be set to 1")
|
|
||||||
end
|
|
||||||
|
|
||||||
print "building glue..."
|
|
||||||
sh.go("build"):print()
|
|
||||||
sh.upx("--ultra-brute", "glue"):print()
|
|
||||||
sh.box("box.rb"):print()
|
|
||||||
|
|
||||||
print "releasing to docker hub"
|
|
||||||
sh.docker("push", "xena/glue"):print()
|
|
||||||
|
|
||||||
print "moving glue binary to $GOPATH/bin"
|
|
||||||
sh.mv("glue", (os.getenv("GOPATH") .. "/bin/glue"))
|
|
||||||
|
|
||||||
print "build/release complete"
|
|
|
@ -1,215 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"runtime/pprof"
|
|
||||||
|
|
||||||
"github.com/Xe/tools/glue/libs/gluaexpect"
|
|
||||||
"github.com/Xe/tools/glue/libs/gluasimplebox"
|
|
||||||
"github.com/ailncode/gluaxmlpath"
|
|
||||||
"github.com/cjoudrey/gluahttp"
|
|
||||||
"github.com/cjoudrey/gluaurl"
|
|
||||||
"github.com/kohkimakimoto/gluaenv"
|
|
||||||
"github.com/kohkimakimoto/gluafs"
|
|
||||||
"github.com/kohkimakimoto/gluamarkdown"
|
|
||||||
"github.com/kohkimakimoto/gluaquestion"
|
|
||||||
"github.com/kohkimakimoto/gluassh"
|
|
||||||
"github.com/kohkimakimoto/gluatemplate"
|
|
||||||
"github.com/kohkimakimoto/gluayaml"
|
|
||||||
"github.com/otm/gluaflag"
|
|
||||||
"github.com/otm/gluash"
|
|
||||||
"github.com/yuin/gluare"
|
|
||||||
"github.com/yuin/gopher-lua"
|
|
||||||
"github.com/yuin/gopher-lua/parse"
|
|
||||||
json "layeh.com/gopher-json"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
os.Exit(mainAux())
|
|
||||||
}
|
|
||||||
|
|
||||||
func mainAux() int {
|
|
||||||
var opt_e, opt_l, opt_p string
|
|
||||||
var opt_i, opt_v, opt_dt, opt_dc bool
|
|
||||||
var opt_m int
|
|
||||||
flag.StringVar(&opt_e, "e", "", "")
|
|
||||||
flag.StringVar(&opt_l, "l", "", "")
|
|
||||||
flag.StringVar(&opt_p, "p", "", "")
|
|
||||||
flag.IntVar(&opt_m, "mx", 0, "")
|
|
||||||
flag.BoolVar(&opt_i, "i", false, "")
|
|
||||||
flag.BoolVar(&opt_v, "v", false, "")
|
|
||||||
flag.BoolVar(&opt_dt, "dt", false, "")
|
|
||||||
flag.BoolVar(&opt_dc, "dc", false, "")
|
|
||||||
flag.Usage = func() {
|
|
||||||
fmt.Println(`Usage: glue [options] [script [args]].
|
|
||||||
Available options are:
|
|
||||||
-e stat execute string 'stat'
|
|
||||||
-l name require library 'name'
|
|
||||||
-mx MB memory limit(default: unlimited)
|
|
||||||
-dt dump AST trees
|
|
||||||
-dc dump VM codes
|
|
||||||
-i enter interactive mode after executing 'script'
|
|
||||||
-p file write cpu profiles to the file
|
|
||||||
-v show version information
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
flag.Parse()
|
|
||||||
if len(opt_p) != 0 {
|
|
||||||
f, err := os.Create(opt_p)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
pprof.StartCPUProfile(f)
|
|
||||||
defer pprof.StopCPUProfile()
|
|
||||||
}
|
|
||||||
if len(opt_e) == 0 && !opt_i && !opt_v && flag.NArg() == 0 {
|
|
||||||
opt_i = true
|
|
||||||
}
|
|
||||||
|
|
||||||
status := 0
|
|
||||||
|
|
||||||
L := lua.NewState()
|
|
||||||
defer L.Close()
|
|
||||||
if opt_m > 0 {
|
|
||||||
L.SetMx(opt_m)
|
|
||||||
}
|
|
||||||
|
|
||||||
preload(L)
|
|
||||||
|
|
||||||
if opt_v || opt_i {
|
|
||||||
fmt.Println(lua.PackageCopyRight)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opt_l) > 0 {
|
|
||||||
if err := L.DoFile(opt_l); err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if nargs := flag.NArg(); nargs > 0 {
|
|
||||||
script := flag.Arg(0)
|
|
||||||
argtb := L.NewTable()
|
|
||||||
for i := 1; i < nargs; i++ {
|
|
||||||
L.RawSet(argtb, lua.LNumber(i), lua.LString(flag.Arg(i)))
|
|
||||||
}
|
|
||||||
L.SetGlobal("arg", argtb)
|
|
||||||
if opt_dt || opt_dc {
|
|
||||||
file, err := os.Open(script)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
chunk, err2 := parse.Parse(file, script)
|
|
||||||
if err2 != nil {
|
|
||||||
fmt.Println(err2.Error())
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if opt_dt {
|
|
||||||
fmt.Println(parse.Dump(chunk))
|
|
||||||
}
|
|
||||||
if opt_dc {
|
|
||||||
proto, err3 := lua.Compile(chunk, script)
|
|
||||||
if err3 != nil {
|
|
||||||
fmt.Println(err3.Error())
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
fmt.Println(proto.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := L.DoFile(script); err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
status = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opt_e) > 0 {
|
|
||||||
if err := L.DoString(opt_e); err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
status = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt_i {
|
|
||||||
doREPL(L)
|
|
||||||
}
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
func preload(L *lua.LState) {
|
|
||||||
L.PreloadModule("re", gluare.Loader)
|
|
||||||
L.PreloadModule("sh", gluash.Loader)
|
|
||||||
L.PreloadModule("markdown", gluamarkdown.Loader)
|
|
||||||
L.PreloadModule("fs", gluafs.Loader)
|
|
||||||
L.PreloadModule("env", gluaenv.Loader)
|
|
||||||
L.PreloadModule("yaml", gluayaml.Loader)
|
|
||||||
L.PreloadModule("question", gluaquestion.Loader)
|
|
||||||
L.PreloadModule("ssh", gluassh.Loader)
|
|
||||||
L.PreloadModule("http", gluahttp.NewHttpModule(&http.Client{}).Loader)
|
|
||||||
L.PreloadModule("flag", gluaflag.Loader)
|
|
||||||
L.PreloadModule("template", gluatemplate.Loader)
|
|
||||||
L.PreloadModule("url", gluaurl.Loader)
|
|
||||||
gluaexpect.Preload(L)
|
|
||||||
gluasimplebox.Preload(L)
|
|
||||||
gluaxmlpath.Preload(L)
|
|
||||||
json.Preload(L)
|
|
||||||
}
|
|
||||||
|
|
||||||
// do read/eval/print/loop
|
|
||||||
func doREPL(L *lua.LState) {
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
for {
|
|
||||||
if str, err := loadline(reader, L); err == nil {
|
|
||||||
if err := L.DoString(str); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
} else { // error on loadline
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func incomplete(err error) bool {
|
|
||||||
if lerr, ok := err.(*lua.ApiError); ok {
|
|
||||||
if perr, ok := lerr.Cause.(*parse.Error); ok {
|
|
||||||
return perr.Pos.Line == parse.EOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadline(reader *bufio.Reader, L *lua.LState) (string, error) {
|
|
||||||
fmt.Print("> ")
|
|
||||||
if line, err := reader.ReadString('\n'); err == nil {
|
|
||||||
if _, err := L.LoadString("return " + line); err == nil { // try add return <...> then compile
|
|
||||||
return line, nil
|
|
||||||
} else {
|
|
||||||
return multiline(line, reader, L)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func multiline(ml string, reader *bufio.Reader, L *lua.LState) (string, error) {
|
|
||||||
for {
|
|
||||||
if _, err := L.LoadString(ml); err == nil { // try compile
|
|
||||||
return ml, nil
|
|
||||||
} else if !incomplete(err) { // syntax error , but not EOF
|
|
||||||
return ml, nil
|
|
||||||
} else {
|
|
||||||
fmt.Print(">> ")
|
|
||||||
if line, err := reader.ReadString('\n'); err == nil {
|
|
||||||
ml = ml + "\n" + line
|
|
||||||
} else {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package gluaexpect
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ThomasRooney/gexpect"
|
|
||||||
lua "github.com/yuin/gopher-lua"
|
|
||||||
luar "layeh.com/gopher-luar"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Preload(L *lua.LState) {
|
|
||||||
L.PreloadModule("expect", Loader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loader is the module loader function.
|
|
||||||
func Loader(L *lua.LState) int {
|
|
||||||
mod := L.SetFuncs(L.NewTable(), api)
|
|
||||||
L.Push(mod)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var api = map[string]lua.LGFunction{
|
|
||||||
"spawn": spawn,
|
|
||||||
}
|
|
||||||
|
|
||||||
func spawn(L *lua.LState) int {
|
|
||||||
cmd := L.CheckString(1)
|
|
||||||
child, err := gexpect.Spawn(cmd)
|
|
||||||
if err != nil {
|
|
||||||
L.Push(lua.LNil)
|
|
||||||
L.Push(lua.LString(err.Error()))
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Push(luar.New(L, child))
|
|
||||||
return 1
|
|
||||||
}
|
|
|
@ -1,100 +0,0 @@
|
||||||
package gluasimplebox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/brandur/simplebox"
|
|
||||||
lua "github.com/yuin/gopher-lua"
|
|
||||||
luar "layeh.com/gopher-luar"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Preload(L *lua.LState) {
|
|
||||||
L.PreloadModule("simplebox", Loader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loader is the module loader function.
|
|
||||||
func Loader(L *lua.LState) int {
|
|
||||||
mod := L.SetFuncs(L.NewTable(), api)
|
|
||||||
L.Push(mod)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var api = map[string]lua.LGFunction{
|
|
||||||
"new": newSecretBox,
|
|
||||||
"genkey": genKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSecretBox(L *lua.LState) int {
|
|
||||||
key := L.CheckString(1)
|
|
||||||
|
|
||||||
k, err := parseKey(key)
|
|
||||||
if err != nil {
|
|
||||||
L.Push(lua.LNil)
|
|
||||||
L.Push(lua.LString(err.Error()))
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
sb := simplebox.NewFromSecretKey(k)
|
|
||||||
|
|
||||||
L.Push(luar.New(L, &box{sb: sb}))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func genKey(L *lua.LState) int {
|
|
||||||
key, err := generateKey()
|
|
||||||
if err != nil {
|
|
||||||
L.Push(lua.LNil)
|
|
||||||
L.Push(lua.LString(err.Error()))
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Push(lua.LString(base64.URLEncoding.EncodeToString(key[:])))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateKey() (*[32]byte, error) {
|
|
||||||
var k [32]byte
|
|
||||||
_, err := rand.Read(k[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &k, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseKey(s string) (*[32]byte, error) {
|
|
||||||
k := &[32]byte{}
|
|
||||||
raw, err := base64.URLEncoding.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if n := copy(k[:], raw); n < len(k) {
|
|
||||||
return nil, errors.New("not valid")
|
|
||||||
}
|
|
||||||
return k, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type box struct {
|
|
||||||
sb *simplebox.SimpleBox
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *box) Encrypt(data string) string {
|
|
||||||
result := b.sb.Encrypt([]byte(data))
|
|
||||||
return hex.EncodeToString(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *box) Decrypt(data string) (string, error) {
|
|
||||||
d, err := hex.DecodeString(data)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
plain, err := b.sb.Decrypt([]byte(d))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(plain), nil
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
imagesize
|
|
||||||
output
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue