early rapid development
This commit is contained in:
@@ -45,6 +45,7 @@ func FireEvent(inst HandlerInst, event any, handlers map[reflect.Type]reflect.Va
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Start(inst HandlerInst) (err error) {
|
func (c *Conn) Start(inst HandlerInst) (err error) {
|
||||||
|
_ = *c // exit early on nil connection
|
||||||
handlers := FindHandlers(inst)
|
handlers := FindHandlers(inst)
|
||||||
err = FireEvent(inst, OnStart{}, handlers)
|
err = FireEvent(inst, OnStart{}, handlers)
|
||||||
for err == nil {
|
for err == nil {
|
||||||
|
|||||||
@@ -5,17 +5,18 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strom"
|
|
||||||
"strom/modules"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom"
|
||||||
|
"github.com/admin-else/strom/modules"
|
||||||
|
|
||||||
"github.com/admin-else/queser/generated/v1_21_8"
|
"github.com/admin-else/queser/generated/v1_21_8"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
*strom.Conn
|
*strom.Conn
|
||||||
Ticker *modules.Ticker
|
Ticker *modules.Timer
|
||||||
Message string
|
Message string
|
||||||
AltAccount strom.Account
|
AltAccount strom.Account
|
||||||
}
|
}
|
||||||
@@ -62,7 +63,7 @@ func spawnSpammer(message, connectTo string, account strom.Account, wg *sync.Wai
|
|||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
c := &Client{Conn: conn, Message: message}
|
c := &Client{Conn: conn, Message: message}
|
||||||
t := modules.Ticker{}
|
t := modules.Timer{}
|
||||||
t.Interval = append(t.Interval, modules.IntervalTask{
|
t.Interval = append(t.Interval, modules.IntervalTask{
|
||||||
F: c.Spam,
|
F: c.Spam,
|
||||||
Interval: time.Second,
|
Interval: time.Second,
|
||||||
|
|||||||
+15
-2
@@ -1,10 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"strom"
|
|
||||||
|
"github.com/admin-else/strom"
|
||||||
|
|
||||||
"github.com/admin-else/queser"
|
"github.com/admin-else/queser"
|
||||||
"github.com/admin-else/queser/generated/v1_21_8"
|
"github.com/admin-else/queser/generated/v1_21_8"
|
||||||
@@ -15,6 +17,10 @@ type ProxyClient struct {
|
|||||||
Proxy *Proxy
|
Proxy *Proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *ProxyClient) OnLoopCycle(event strom.OnLoopCycle) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (p *ProxyClient) Default(event any) (err error) {
|
func (p *ProxyClient) Default(event any) (err error) {
|
||||||
fmt.Printf("S2C %#v\n", event)
|
fmt.Printf("S2C %#v\n", event)
|
||||||
err = p.Proxy.Send(event)
|
err = p.Proxy.Send(event)
|
||||||
@@ -43,8 +49,12 @@ type Proxy struct {
|
|||||||
ProxyClient *ProxyClient
|
ProxyClient *ProxyClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) OnLastTrigger(event strom.OnLoopCycle) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Proxy) Default(event any) (err error) {
|
func (p *Proxy) Default(event any) (err error) {
|
||||||
fmt.Printf("C2S LastTrigger%#v\n", event)
|
fmt.Printf("C2S %#v\n", event)
|
||||||
err = p.ProxyClient.Send(event)
|
err = p.ProxyClient.Send(event)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -54,6 +64,9 @@ func (p *Proxy) OnHandshake(packet v1_21_8.HandshakingToServerPacketSetProtocol)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if packet.NextState != queser.VarInt(queser.Status) && packet.NextState != queser.VarInt(queser.Login) {
|
||||||
|
err = errors.New("invalid next state")
|
||||||
|
}
|
||||||
p.State = queser.State(packet.NextState)
|
p.State = queser.State(packet.NextState)
|
||||||
p.ProxyClient.State = queser.State(packet.NextState)
|
p.ProxyClient.State = queser.State(packet.NextState)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto"
|
|
||||||
cryptorand "crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"math/rand/v2"
|
|
||||||
"net/http"
|
|
||||||
"strom"
|
|
||||||
"strom/modules"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/admin-else/queser/generated/v1_21_8"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
*strom.Conn
|
|
||||||
Ticker *modules.Ticker
|
|
||||||
Message string
|
|
||||||
AltAccount strom.Account
|
|
||||||
|
|
||||||
SessionId uuid.UUID
|
|
||||||
PrivateKey *rsa.PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) Default(event any) (err error) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) MakeChatSession() (err error) {
|
|
||||||
c.SessionId, err = uuid.NewRandom()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
keys, err := c.AltAccount.FetchKeys()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
block, _ := pem.Decode([]byte(keys.KeyPair.PrivateKey))
|
|
||||||
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.PrivateKey = privateKey.(*rsa.PrivateKey)
|
|
||||||
|
|
||||||
x509encoded, _ := pem.Decode([]byte(keys.KeyPair.PublicKey))
|
|
||||||
sigBytes, err := base64.StdEncoding.DecodeString(keys.PublicKeySignatureV2)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
packet := v1_21_8.PlayToServerPacketChatSessionUpdate{
|
|
||||||
SessionUUID: c.SessionId,
|
|
||||||
ExpireTime: keys.ExpiresAt.UnixMilli(),
|
|
||||||
PublicKey: v1_21_8.ByteArray{Val: x509encoded.Bytes},
|
|
||||||
Signature: v1_21_8.ByteArray{Val: sigBytes},
|
|
||||||
}
|
|
||||||
err = c.Send(packet)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) MakeMessageSum(salt, timestamp int64, message string) (sum []byte, err error) {
|
|
||||||
h := sha256.New()
|
|
||||||
err = binary.Write(h, binary.BigEndian, int32(1))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = h.Write(c.AltAccount.Uuid[:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = h.Write(c.SessionId[:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Write(h, binary.BigEndian, int32(0)) // message index unimplemented
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Write(h, binary.BigEndian, salt)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Write(h, binary.BigEndian, timestamp)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Write(h, binary.BigEndian, int32(len(message)))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = h.Write([]byte(message))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Write(h, binary.BigEndian, int32(0)) // number of last seen messages unimplemented
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sum = h.Sum(nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) SendReportableChat(message string) (err error) {
|
|
||||||
salt := rand.Int64()
|
|
||||||
timestamp := time.Now().UnixMilli()
|
|
||||||
sum, err := c.MakeMessageSum(salt, timestamp, message)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
signature, err := rsa.SignPSS(cryptorand.Reader, c.PrivateKey, crypto.SHA256, sum, nil)
|
|
||||||
return c.Send(v1_21_8.PlayToServerPacketChatMessage{Message: message, Timestamp: timestamp, Salt: salt, Signature: (*[256]byte)(signature), Offset: 0, Acknowledged: [3]uint8{0x0, 0x0, 0x0}, Checksum: 0x1})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) OnStart(_ strom.OnStart) (err error) {
|
|
||||||
err = modules.Login(c.Conn, c.AltAccount)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = modules.IgnoreConfig(c.Conn)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = c.MakeChatSession()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Ticker.Active = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) OnLoopCycle(strom.OnLoopCycle) (err error) {
|
|
||||||
err = c.Ticker.Tick()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) KeepAlive(packet v1_21_8.PlayToClientPacketKeepAlive) (err error) {
|
|
||||||
return c.Send(v1_21_8.PlayToServerPacketKeepAlive(packet))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) Spam() (err error) {
|
|
||||||
err = c.SendReportableChat(c.Message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func DownloadAccounts(token string) (accounts []strom.Account, err error) {
|
|
||||||
r, err := http.NewRequest("GET", "https://griefing.homes/api/list/active", nil)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.Header.Set("Accept", "application/json")
|
|
||||||
r.Header.Set("Authorization", token)
|
|
||||||
resp, err := http.DefaultClient.Do(r)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
b := bytes.NewBuffer(nil)
|
|
||||||
_, err = b.ReadFrom(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(b.Bytes(), &accounts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
conn, err := strom.Connect("127.0.0.1:25566")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
c := &Client{Conn: conn, Message: "LOBA ONTOP XD"}
|
|
||||||
t := modules.Ticker{}
|
|
||||||
t.Interval = append(t.Interval, modules.IntervalTask{
|
|
||||||
F: c.Spam,
|
|
||||||
Interval: time.Second,
|
|
||||||
})
|
|
||||||
c.Ticker = &t
|
|
||||||
c.AltAccount = strom.Account{
|
|
||||||
Name: "blossom_varJ5_11",
|
|
||||||
Uuid: uuid.MustParse("28ea954b7a9144d192081d686df32e56"),
|
|
||||||
Ygg: "eyJraWQiOiIwNDkxODEiLCJhbGciOiJSUzI1NiJ9.eyJ4dWlkIjoiMjUzNTQ2NjA5NDkzNTk1NyIsImFnZyI6IkFkdWx0Iiwic3ViIjoiODRmY2JhZTgtNmZlMS00ZDc5LWI4NTMtOGMzNGIwYzI5MGM2IiwiYXV0aCI6IlhCT1giLCJucyI6ImRlZmF1bHQiLCJyb2xlcyI6W10sImlzcyI6ImF1dGhlbnRpY2F0aW9uIiwiZmxhZ3MiOlsidHdvZmFjdG9yYXV0aCIsIm11bHRpcGxheWVyIiwib3JkZXJzXzIwMjIiLCJtc2FtaWdyYXRpb25fc3RhZ2U0Il0sInByb2ZpbGVzIjp7Im1jIjoiMjhlYTk1NGItN2E5MS00NGQxLTkyMDgtMWQ2ODZkZjMyZTU2In0sInBsYXRmb3JtIjoiUENfTEFVTkNIRVIiLCJwZmQiOlt7InR5cGUiOiJtYyIsImlkIjoiMjhlYTk1NGItN2E5MS00NGQxLTkyMDgtMWQ2ODZkZjMyZTU2IiwibmFtZSI6ImJsb3Nzb21fdmFySjVfMTEifV0sIm5iZiI6MTc2NDQ0ODE4NywiZXhwIjoxNzY0NTM0NTg3LCJpYXQiOjE3NjQ0NDgxODcsImFpZCI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDA0YzEyYWU2ZiJ9.EC2LOt06Q4L9gyhufn97y2bqqzqJfv_kSQCdbRUNNyfhTTKonAlgi9LFv-OKN2sCcA96azTEQzpAiOTvL5jQ4H6nFT8sFz6kTMxD7vmii7ztCsCRrshJeZmW7mPCEn8WNpZukXbxVcaOhyjrNwA47RXEm-GazuPheDpWi_fvDSnY1vsLLk4ouIoW_r3kwA9bRakCJRF6r-p0feyi-bJamjLq2yDp7SEyZRVLeBhNA6pxVVU0WtyP6mgVO4cg0XeGwlMpktcs4IiYaD2C7AFjvedCdy7WJQOImLIcsc-gV4kFOn6oFROEf3EGN0RDcsrrlOJalAM01-rZTve6tUKGZQ",
|
|
||||||
}
|
|
||||||
|
|
||||||
keys, err := c.AltAccount.FetchKeys()
|
|
||||||
fmt.Println(keys)
|
|
||||||
err = conn.Start(c)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
module strom
|
module github.com/admin-else/strom
|
||||||
|
|
||||||
replace github.com/admin-else/queser => ../queser
|
replace github.com/admin-else/queser => ../queser
|
||||||
|
|
||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
require github.com/admin-else/queser v0.0.0-00010101000000-000000000000
|
require github.com/admin-else/queser v0.0.0-20251204142744-84a884c09415
|
||||||
|
|
||||||
require github.com/google/uuid v1.6.0 // indirect
|
require github.com/google/uuid v1.6.0
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package modules
|
package modules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strom"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom"
|
||||||
|
|
||||||
"github.com/admin-else/queser"
|
"github.com/admin-else/queser"
|
||||||
"github.com/admin-else/queser/generated/v1_21_8"
|
"github.com/admin-else/queser/generated/v1_21_8"
|
||||||
@@ -12,6 +15,7 @@ type ConfigIgnorer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigIgnorer) Default(event any) (err error) {
|
func (c *ConfigIgnorer) Default(event any) (err error) {
|
||||||
|
fmt.Printf("%#v\n", event)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +33,20 @@ func (c *ConfigIgnorer) OnFinish(packet v1_21_8.ConfigurationToClientPacketFinis
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ConfigIgnorer) OnPing(packet v1_21_8.ConfigurationToClientPacketPing) (err error) {
|
||||||
|
err = c.Send(v1_21_8.ConfigurationToServerPacketPong(packet))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConfigIgnorer) OnKeepAlive(packet v1_21_8.ConfigurationToClientPacketKeepAlive) (err error) {
|
||||||
|
err = c.Send(v1_21_8.ConfigurationToServerPacketKeepAlive(packet))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func IgnoreConfig(c *strom.Conn) (err error) {
|
func IgnoreConfig(c *strom.Conn) (err error) {
|
||||||
return c.Start(&ConfigIgnorer{c})
|
err = c.Start(&ConfigIgnorer{c})
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Join(err, errors.New("failed to ignore config"))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-5
@@ -8,10 +8,13 @@ import (
|
|||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"strom"
|
|
||||||
|
"github.com/admin-else/strom"
|
||||||
|
|
||||||
"github.com/admin-else/queser"
|
"github.com/admin-else/queser"
|
||||||
"github.com/admin-else/queser/data"
|
"github.com/admin-else/queser/data"
|
||||||
@@ -35,18 +38,21 @@ func (s *LoginClient) OnCycle(_ strom.OnLoopCycle) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginClient) OnStart(_ strom.OnStart) (err error) {
|
func (s *LoginClient) OnStart(_ strom.OnStart) (err error) {
|
||||||
parts := strings.Split(s.RemoteAddr().String(), ":")
|
host, portStr, err := net.SplitHostPort(s.RemoteAddr().String())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
versionData, err := data.LookUpProtocolVersionByName(s.Version)
|
versionData, err := data.LookUpProtocolVersionByName(s.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
port, err := strconv.Atoi(parts[1])
|
port, err := strconv.Atoi(portStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = s.Send(v1_21_8.HandshakingToServerPacketSetProtocol{
|
err = s.Send(v1_21_8.HandshakingToServerPacketSetProtocol{
|
||||||
ProtocolVersion: queser.VarInt(versionData.Version),
|
ProtocolVersion: queser.VarInt(versionData.Version),
|
||||||
ServerHost: parts[0],
|
ServerHost: host,
|
||||||
ServerPort: uint16(port),
|
ServerPort: uint16(port),
|
||||||
NextState: queser.VarInt(queser.Login),
|
NextState: queser.VarInt(queser.Login),
|
||||||
})
|
})
|
||||||
@@ -164,8 +170,12 @@ func (s *LoginClient) OnSuccess(success v1_21_8.LoginToClientPacketSuccess) (err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Login(c *strom.Conn, account strom.Account) (err error) {
|
func Login(c *strom.Conn, account strom.Account) (err error) {
|
||||||
return c.Start(&LoginClient{
|
err = c.Start(&LoginClient{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
Account: account,
|
Account: account,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Join(err, errors.New("failed to login"))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -4,7 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"strom"
|
|
||||||
|
"github.com/admin-else/strom"
|
||||||
|
|
||||||
"github.com/admin-else/queser"
|
"github.com/admin-else/queser"
|
||||||
"github.com/admin-else/queser/data"
|
"github.com/admin-else/queser/data"
|
||||||
|
|||||||
+16
-2
@@ -13,13 +13,13 @@ type OnceTask struct {
|
|||||||
Trigger time.Time
|
Trigger time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type Ticker struct {
|
type Timer struct {
|
||||||
Once []OnceTask
|
Once []OnceTask
|
||||||
Interval []IntervalTask
|
Interval []IntervalTask
|
||||||
Active bool
|
Active bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Ticker) Tick() (err error) {
|
func (t *Timer) Tick() (err error) {
|
||||||
if !t.Active {
|
if !t.Active {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -47,3 +47,17 @@ func (t *Ticker) Tick() (err error) {
|
|||||||
t.Once = triggerLater
|
t.Once = triggerLater
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Timer) Every(interval time.Duration, f func() error) {
|
||||||
|
t.Interval = append(t.Interval, IntervalTask{
|
||||||
|
F: f,
|
||||||
|
Interval: interval,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Timer) In(interval time.Duration, f func() error) {
|
||||||
|
t.Once = append(t.Once, OnceTask{
|
||||||
|
F: f,
|
||||||
|
Trigger: time.Now().Add(interval),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user