Refactor: Simplify LoginClient, replace Account struct with strom.Account, add Ticker module, and enhance configuration handling
This commit is contained in:
+107
@@ -0,0 +1,107 @@
|
|||||||
|
package strom
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
Name string
|
||||||
|
Uuid uuid.UUID
|
||||||
|
Ygg string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ApiBase = "https://api.minecraftservices.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *Account) doMcApiRequest(method string, url string, from any, to any) (err error) {
|
||||||
|
var b []byte
|
||||||
|
if method != "GET" {
|
||||||
|
b, err = json.Marshal(from)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := http.NewRequest(method, url, bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Header.Set("Content-Type", "application/json")
|
||||||
|
r.Header.Set("Accept", "application/json")
|
||||||
|
r.Header.Set("x-xbl-contract-version", "1")
|
||||||
|
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", a.Ygg))
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resp.StatusCode >= 200 && resp.StatusCode <= 299) {
|
||||||
|
return fmt.Errorf("bad status code %v", resp.StatusCode)
|
||||||
|
}
|
||||||
|
b, err = io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(b, to)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlayerKeys struct {
|
||||||
|
ExpiresAt time.Time `json:"expiresAt"`
|
||||||
|
KeyPair struct {
|
||||||
|
PublicKey string `json:"publicKey"`
|
||||||
|
PrivateKey string `json:"privateKey"`
|
||||||
|
} `json:"keyPair"`
|
||||||
|
PublicKeySignature string `json:"publicKeySignature"`
|
||||||
|
PublicKeySignatureV2 string `json:"publicKeySignatureV2"`
|
||||||
|
RefreshedAfter time.Time `json:"refreshedAfter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Account) FetchKeys() (keys PlayerKeys, err error) {
|
||||||
|
err = a.doMcApiRequest("POST", ApiBase+"/player/certificates", nil, &keys)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Account) JoinServer(serverId string) (err error) {
|
||||||
|
var body []byte
|
||||||
|
body, err = json.Marshal(struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
SelectedProfile string `json:"selectedProfile"`
|
||||||
|
ServerId string `json:"serverId"`
|
||||||
|
}{
|
||||||
|
AccessToken: a.Ygg,
|
||||||
|
SelectedProfile: strings.ReplaceAll(a.Uuid.String(), "-", ""),
|
||||||
|
ServerId: serverId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var resp *http.Response
|
||||||
|
resp, err = http.Post("https://sessionserver.mojang.com/session/minecraft/join", "application/json", bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 204 {
|
||||||
|
body, err = io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("bad client code %v body %v", resp.StatusCode, string(body))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+5
-4
@@ -15,8 +15,9 @@ var BadPacketTypeError = errors.New("bad packet type")
|
|||||||
|
|
||||||
// UnCodablePacket represents a packet that could not be decoded due to querser not supporting all packets.
|
// UnCodablePacket represents a packet that could not be decoded due to querser not supporting all packets.
|
||||||
type UnCodablePacket struct {
|
type UnCodablePacket struct {
|
||||||
Err error
|
Err error
|
||||||
Data []byte
|
Partial any
|
||||||
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type Actor int
|
type Actor int
|
||||||
@@ -148,8 +149,8 @@ func (c *Conn) Receive() (packet any, err error) {
|
|||||||
packetBuff := bytes.NewBuffer(packetBytes)
|
packetBuff := bytes.NewBuffer(packetBytes)
|
||||||
packet, err = generated.DecodePacket(c.Version, c.Actor.ReceiveDirection(), c.State, packetBuff)
|
packet, err = generated.DecodePacket(c.Version, c.Actor.ReceiveDirection(), c.State, packetBuff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
packet = UnCodablePacket{Err: err, Data: packetBytes}
|
packet = UnCodablePacket{Err: err, Data: packetBytes, Partial: packet}
|
||||||
return
|
err = nil
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type HandlerInst interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OnStart struct{}
|
type OnStart struct{}
|
||||||
|
type OnLoopCycle struct{}
|
||||||
|
|
||||||
var HandlerDone = errors.New("handler done")
|
var HandlerDone = errors.New("handler done")
|
||||||
|
|
||||||
@@ -53,6 +54,13 @@ func (c *Conn) Start(inst HandlerInst) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = FireEvent(inst, event, handlers)
|
err = FireEvent(inst, event, handlers)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err = FireEvent(inst, OnLoopCycle{}, handlers)
|
||||||
|
}
|
||||||
|
if errors.Is(err, HandlerDone) {
|
||||||
|
err = nil
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+83
-18
@@ -1,45 +1,110 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"bytes"
|
||||||
"fmt"
|
"encoding/json"
|
||||||
|
"math/rand/v2"
|
||||||
|
"net/http"
|
||||||
"strom"
|
"strom"
|
||||||
"strom/modules"
|
"strom/modules"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/admin-else/queser/generated/v1_21_8"
|
||||||
)
|
)
|
||||||
|
|
||||||
var altAccount = modules.Account{
|
|
||||||
Username: "laibergraceful70",
|
|
||||||
Uuid: uuid.MustParse("3a863af533e5452591e913e021bfa724"),
|
|
||||||
Token: "eyJraWQiOiIwNDkxODEiLCJhbGciOiJSUzI1NiJ9.eyJ4dWlkIjoiMjc3NzIxOTc3MDIzMjM1OCIsImFnZyI6IkFkdWx0Iiwic3ViIjoiMzczMjI0OTktZTAwYS0…JRnCbAsBJl5zg-GAu6NkX1JCvpWdQkgFb0H5RYi2dIUKhNpP57WG2gh562T3BoPELVXDQZQVUBzDFeDuVDGGSkKmSmhzO7l-fEPBt2CjnaLCyr1FmYFSRzDBy1w",
|
|
||||||
}
|
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
*strom.Conn
|
*strom.Conn
|
||||||
|
Ticker *modules.Ticker
|
||||||
|
Message string
|
||||||
|
AltAccount strom.Account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Default(event any) (err error) {
|
func (c *Client) Default(event any) (err error) {
|
||||||
fmt.Printf("%T%v\n", event, event)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) SendUnReportableChat(message string) (err error) {
|
||||||
|
return c.Send(v1_21_8.PlayToServerPacketChatMessage{Message: message, Timestamp: time.Now().Unix(), Salt: rand.Int64(), Signature: nil, Offset: 0, Acknowledged: [3]uint8{0x0, 0x0, 0x0}, Checksum: 0x1})
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) OnStart(_ strom.OnStart) (err error) {
|
func (c *Client) OnStart(_ strom.OnStart) (err error) {
|
||||||
err = modules.Login(c.Conn, altAccount)
|
err = modules.Login(c.Conn, c.AltAccount)
|
||||||
if errors.Is(err, strom.HandlerDone) {
|
if err != nil {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
err = modules.IgnoreConfig(c.Conn)
|
||||||
|
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.SendUnReportableChat(c.Message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func spawnSpammer(message, connectTo string, account strom.Account, wg *sync.WaitGroup) {
|
||||||
|
conn, err := strom.Connect(connectTo)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
c := &Client{Conn: conn, Message: message}
|
||||||
|
t := modules.Ticker{}
|
||||||
|
t.Interval = append(t.Interval, modules.IntervalTask{
|
||||||
|
F: c.Spam,
|
||||||
|
Interval: time.Second,
|
||||||
|
})
|
||||||
|
c.Ticker = &t
|
||||||
|
c.AltAccount = account
|
||||||
|
err = conn.Start(c)
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c, err := strom.Connect("127.0.0.1:25566")
|
accounts, err := DownloadAccounts("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzNDBjNWIzMS02MmEzLTRjODUtYTE5ZC1lZjcxMWVkYzY3ZWQifQ.M_gwEK2HyyY6d4RFY7JzyeuqGVvmqmPK-13wNP0AxGc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
|
||||||
err = c.Start(&Client{c})
|
wg := &sync.WaitGroup{}
|
||||||
if err != nil {
|
wg.Add(len(accounts))
|
||||||
panic(err)
|
for _, account := range accounts {
|
||||||
|
go spawnSpammer("LOBA ONTOP XD", "127.0.0.1:25566", account, wg)
|
||||||
|
time.Sleep(time.Second * 6)
|
||||||
}
|
}
|
||||||
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,11 @@ type ProxyClient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *ProxyClient) Default(event any) (err error) {
|
func (p *ProxyClient) Default(event any) (err error) {
|
||||||
fmt.Printf("1 %T%v\n", event, event)
|
fmt.Printf("S2C %#v\n", event)
|
||||||
err = p.Proxy.Send(event)
|
err = p.Proxy.Send(event)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ type Proxy struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proxy) Default(event any) (err error) {
|
func (p *Proxy) Default(event any) (err error) {
|
||||||
fmt.Printf("2 %T%v\n", event, event)
|
fmt.Printf("C2S LastTrigger%#v\n", event)
|
||||||
err = p.ProxyClient.Send(event)
|
err = p.ProxyClient.Send(event)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -91,10 +94,10 @@ func handleConn(conn net.Conn) {
|
|||||||
p.ProxyClient = &ProxyClient{Conn: c, Proxy: p}
|
p.ProxyClient = &ProxyClient{Conn: c, Proxy: p}
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
errChan <- c.Start(p.ProxyClient)
|
errChan <- fmt.Errorf("client error: %v", c.Start(p.ProxyClient))
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
errChan <- s.Start(p)
|
errChan <- fmt.Errorf("server error: %v", s.Start(p))
|
||||||
}()
|
}()
|
||||||
err = <-errChan
|
err = <-errChan
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,7 +1,34 @@
|
|||||||
package modules
|
package modules
|
||||||
|
|
||||||
import "strom"
|
import (
|
||||||
|
"strom"
|
||||||
|
|
||||||
func IgnoreConfig(c *strom.Conn) (err error) {
|
"github.com/admin-else/queser"
|
||||||
|
"github.com/admin-else/queser/generated/v1_21_8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigIgnorer struct {
|
||||||
|
*strom.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConfigIgnorer) Default(event any) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ConfigIgnorer) OnKnownPacks(packet v1_21_8.PacketCommonSelectKnownPacks) (err error) {
|
||||||
|
return c.Send(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConfigIgnorer) OnFinish(packet v1_21_8.ConfigurationToClientPacketFinishConfiguration) (err error) {
|
||||||
|
err = c.Send(v1_21_8.ConfigurationToServerPacketFinishConfiguration{})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.State = queser.Play
|
||||||
|
err = strom.HandlerDone
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func IgnoreConfig(c *strom.Conn) (err error) {
|
||||||
|
return c.Start(&ConfigIgnorer{c})
|
||||||
|
}
|
||||||
|
|||||||
+10
-46
@@ -1,7 +1,6 @@
|
|||||||
package modules
|
package modules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@@ -9,10 +8,7 @@ import (
|
|||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"strom"
|
"strom"
|
||||||
@@ -20,23 +16,21 @@ import (
|
|||||||
"github.com/admin-else/queser"
|
"github.com/admin-else/queser"
|
||||||
"github.com/admin-else/queser/data"
|
"github.com/admin-else/queser/data"
|
||||||
"github.com/admin-else/queser/generated/v1_21_8"
|
"github.com/admin-else/queser/generated/v1_21_8"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Account struct {
|
|
||||||
Username string
|
|
||||||
Uuid uuid.UUID
|
|
||||||
Token string
|
|
||||||
}
|
|
||||||
type LoginClient struct {
|
type LoginClient struct {
|
||||||
*strom.Conn
|
*strom.Conn
|
||||||
Account Account
|
Account strom.Account
|
||||||
|
|
||||||
GivenAccount v1_21_8.LoginToClientPacketSuccess
|
GivenAccount v1_21_8.LoginToClientPacketSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginClient) Default(event any) (err error) {
|
func (s *LoginClient) Default(event any) (err error) {
|
||||||
err = fmt.Errorf("unexpected event: %T%v", event, event)
|
err = fmt.Errorf("unexpected event: %#v", event)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LoginClient) OnCycle(_ strom.OnLoopCycle) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +54,7 @@ func (s *LoginClient) OnStart(_ strom.OnStart) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.State = queser.Login
|
s.State = queser.Login
|
||||||
err = s.Send(v1_21_8.LoginToServerPacketLoginStart{Username: s.Account.Username, PlayerUUID: s.Account.Uuid})
|
err = s.Send(v1_21_8.LoginToServerPacketLoginStart{Username: s.Account.Name, PlayerUUID: s.Account.Uuid})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,47 +96,17 @@ func AuthDigest(elems ...[]byte) string {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func JoinServerAPI(a Account, serverId string) (err error) {
|
|
||||||
var body []byte
|
|
||||||
body, err = json.Marshal(struct {
|
|
||||||
AccessToken string `json:"accessToken"`
|
|
||||||
SelectedProfile string `json:"selectedProfile"`
|
|
||||||
ServerId string `json:"serverId"`
|
|
||||||
}{
|
|
||||||
AccessToken: a.Token,
|
|
||||||
SelectedProfile: strings.ReplaceAll(a.Uuid.String(), "-", ""),
|
|
||||||
ServerId: serverId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var resp *http.Response
|
|
||||||
resp, err = http.Post("https://sessionserver.mojang.com/session/minecraft/join", "application/json", bytes.NewReader(body))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if resp.StatusCode != 204 {
|
|
||||||
body, err = io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = fmt.Errorf("bad client code %v body %v", resp.StatusCode, string(body))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LoginClient) OnEncrypt(packet v1_21_8.LoginToClientPacketEncryptionBegin) (err error) {
|
func (s *LoginClient) OnEncrypt(packet v1_21_8.LoginToClientPacketEncryptionBegin) (err error) {
|
||||||
sharedSecret := make([]byte, 16)
|
sharedSecret := make([]byte, 16)
|
||||||
_, _ = rand.Read(sharedSecret) //never fails
|
_, _ = rand.Read(sharedSecret) //never fails
|
||||||
|
|
||||||
if packet.ShouldAuthenticate {
|
if packet.ShouldAuthenticate {
|
||||||
if s.Account.Token == "" {
|
if s.Account.Ygg == "" {
|
||||||
err = fmt.Errorf("the account has no token so we cant join online servers")
|
err = fmt.Errorf("the account has no token so we cant join online servers")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serverId := AuthDigest([]byte(packet.ServerId), sharedSecret, packet.PublicKey)
|
serverId := AuthDigest([]byte(packet.ServerId), sharedSecret, packet.PublicKey)
|
||||||
err = JoinServerAPI(s.Account, serverId)
|
err = s.Account.JoinServer(serverId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -199,7 +163,7 @@ func (s *LoginClient) OnSuccess(success v1_21_8.LoginToClientPacketSuccess) (err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Login(c *strom.Conn, account Account) (err error) {
|
func Login(c *strom.Conn, account strom.Account) (err error) {
|
||||||
return c.Start(&LoginClient{
|
return c.Start(&LoginClient{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
Account: account,
|
Account: account,
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package modules
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type IntervalTask struct {
|
||||||
|
F func() error
|
||||||
|
LastTrigger time.Time
|
||||||
|
Interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type OnceTask struct {
|
||||||
|
F func() error
|
||||||
|
Trigger time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ticker struct {
|
||||||
|
Once []OnceTask
|
||||||
|
Interval []IntervalTask
|
||||||
|
Active bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Ticker) Tick() (err error) {
|
||||||
|
if !t.Active {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, task := range t.Interval {
|
||||||
|
if time.Since(task.LastTrigger) >= task.Interval {
|
||||||
|
task.LastTrigger = time.Now()
|
||||||
|
err = task.F()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Interval[i] = task
|
||||||
|
}
|
||||||
|
var triggerLater []OnceTask
|
||||||
|
for _, task := range t.Once {
|
||||||
|
if task.Trigger.After(time.Now()) {
|
||||||
|
err = task.F()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
triggerLater = append(triggerLater, task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Once = triggerLater
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package strom
|
||||||
|
|
||||||
|
type SecureChat struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchSecureChat(account Account) {
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user