1
0
mirror of https://github.com/fumiama/tienyik.git synced 2026-06-05 07:20:25 +08:00

feat: add log & supporting packages

This commit is contained in:
源文雨
2025-10-30 23:23:45 +08:00
parent 617fc662c5
commit 15fcc9a338
19 changed files with 672 additions and 12 deletions

18
internal/hcli/api.go Normal file
View File

@@ -0,0 +1,18 @@
package hcli
import (
"strings"
base14 "github.com/fumiama/go-base16384"
"github.com/fumiama/tienyik/internal/log"
)
var eps = base14.DecodeString("栝啇俌蠯姙呗宬籣欞敖蚹煮岎冃勀紀㴆")
func ep(p string) string {
sb := &strings.Builder{}
sb.WriteString(eps)
sb.WriteString(p)
log.Debugln("ep wraps:", sb)
return sb.String()
}

65
internal/hcli/http.go Normal file
View File

@@ -0,0 +1,65 @@
package hcli
import (
"io"
"net/http"
base14 "github.com/fumiama/go-base16384"
)
func setCommonHeaders(req *http.Request) {
req.Header.Set("Accept", "application/json")
req.Header.Set("Pragma", "no-cache")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Origin", base14.DecodeString("栝啇俌蠯姜吲融艹歛烦宸㴅"))
req.Header.Set("Referer", base14.DecodeString("栝啇俌蠯姜吲融艹歛烦宸紀㴆"))
req.Header.Set(
"User-Agent",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0",
)
}
func Get(path string) (resp *http.Response, err error) {
req, err := http.NewRequest(http.MethodGet, ep(path), nil)
if err != nil {
return nil, err
}
setCommonHeaders(req)
return http.DefaultClient.Do(req)
}
func Post(path string, body io.Reader) (resp *http.Response, err error) {
req, err := http.NewRequest(http.MethodPost, ep(path), body)
if err != nil {
return nil, err
}
setCommonHeaders(req)
return http.DefaultClient.Do(req)
}
func Put(path string, body io.Reader) (resp *http.Response, err error) {
req, err := http.NewRequest(http.MethodPut, ep(path), body)
if err != nil {
return nil, err
}
setCommonHeaders(req)
return http.DefaultClient.Do(req)
}
func Delete(path string) (resp *http.Response, err error) {
req, err := http.NewRequest(http.MethodDelete, ep(path), nil)
if err != nil {
return nil, err
}
setCommonHeaders(req)
return http.DefaultClient.Do(req)
}
func Patch(path string, body io.Reader) (resp *http.Response, err error) {
req, err := http.NewRequest(http.MethodPatch, ep(path), body)
if err != nil {
return nil, err
}
setCommonHeaders(req)
return http.DefaultClient.Do(req)
}

22
internal/hson/req.go Normal file
View File

@@ -0,0 +1,22 @@
package hson
import (
"bytes"
"encoding/json"
"github.com/fumiama/tienyik"
"github.com/fumiama/tienyik/internal/log"
)
func Marshal(tya *tienyik.AES, v any) []byte {
w := bytes.NewBuffer(make([]byte, 0, 1024))
err := json.NewEncoder(w).Encode(v)
if err != nil {
panic(err)
}
log.Debugln("Marshal JSON:", w.String())
if tya != nil {
return tya.Encrypt(w.Bytes())
}
return w.Bytes()
}

56
internal/hson/resp.go Normal file
View File

@@ -0,0 +1,56 @@
package hson
import (
"encoding/base64"
"encoding/json"
"errors"
"io"
"strconv"
"github.com/fumiama/tienyik"
"github.com/fumiama/tienyik/internal/log"
)
type responseBase[T any] struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data T `json:"data"`
EData string `json:"edata"`
}
func (rb *responseBase[T]) ok() error {
if rb.Code != 0 {
return errors.New("[" + strconv.Itoa(rb.Code) + "] " + rb.Msg)
}
return nil
}
func Unmarshal[T any](tya *tienyik.AES, r io.Reader) (data T, err error) {
var rsp responseBase[T]
err = json.NewDecoder(r).Decode(&rsp)
if err == nil {
err = rsp.ok()
}
if err != nil {
return
}
if len(rsp.EData) > 0 && tya != nil {
var d []byte
d, err = base64.StdEncoding.DecodeString(rsp.EData)
if err != nil {
return
}
d, err = tya.Decrypt(d)
if err != nil {
return
}
log.Debugln("decrypted data:", string(d))
err = json.Unmarshal(d, &rsp)
if err != nil {
return
}
err = rsp.ok()
}
data = rsp.Data
return
}

3
internal/log/def.go Normal file
View File

@@ -0,0 +1,3 @@
package log
const debug = true

103
internal/log/wrap.go Normal file
View File

@@ -0,0 +1,103 @@
package log
import (
"github.com/sirupsen/logrus"
"github.com/fumiama/tienyik/internal/textio"
)
func Debug(args ...any) {
if debug {
args = append([]any{textio.Logger(2)}, args...)
logrus.Debug(args...)
}
}
func Debugf(format string, args ...any) {
if debug {
args = append([]any{textio.Logger(2)}, args...)
logrus.Debugf(format, args...)
}
}
func Debugln(args ...any) {
if debug {
args = append([]any{textio.Logger(2)}, args...)
logrus.Debugln(args...)
}
}
func Info(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Info(args...)
}
func Infof(format string, args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Infof(format, args...)
}
func Infoln(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Infoln(args...)
}
func Warn(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Warn(args...)
}
func Warnf(format string, args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Warnf(format, args...)
}
func Warnln(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Warnln(args...)
}
func Error(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Error(args...)
}
func Errorf(format string, args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Errorf(format, args...)
}
func Errorln(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Errorln(args...)
}
func Fatal(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Fatal(args...)
}
func Fatalf(format string, args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Fatalf(format, args...)
}
func Fatalln(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Fatalln(args...)
}
func Panic(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Panic(args...)
}
func Panicf(format string, args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Panicf(format, args...)
}
func Panicln(args ...any) {
args = append([]any{textio.Logger(2)}, args...)
logrus.Panicln(args...)
}

8
internal/op/chain.go Normal file
View File

@@ -0,0 +1,8 @@
package op
func Must[T any](x T, err error) T {
if err != nil {
panic(err)
}
return x
}

70
internal/textio/api.go Normal file
View File

@@ -0,0 +1,70 @@
package textio
import (
"errors"
"net/url"
"runtime"
"strings"
"github.com/fumiama/tienyik"
)
func API() string {
pc, f, _, ok := runtime.Caller(1)
if !ok {
panic("cannot get api of caller")
}
if strings.Contains(f, "\\") {
f = strings.ReplaceAll(f, "\\", "/")
}
_, p, ok := strings.Cut(f, "/tienyik/")
if !ok {
panic("cannot cut api " + f + " of caller")
}
f = strings.TrimSuffix(p, ".go")
fn := runtime.FuncForPC(pc)
if fn == nil {
panic("cannot get func name of caller, api: " + f)
}
p = fn.Name()
i := strings.LastIndex(p, ".")
if i < 0 {
panic("func name of caller '" + p + " has no '.', api: " + f)
}
p = p[i+1:]
if len(p) <= 1 {
panic("func name of caller '" + p + " too short', api: " + f)
}
sb := strings.Builder{}
sb.WriteString(f)
sb.WriteByte('/')
sb.WriteString(strings.ToLower(p[:1]))
sb.WriteString(p[1:])
return sb.String()
}
func EUrlParams(tya *tienyik.AES, params url.Values) string {
return url.Values{
FuncName(1, true): {BytesToString(tya.Encrypt(
StringToBytes(params.Encode()),
))},
}.Encode()
}
func ParseQuery(tya *tienyik.AES, eparams string) (url.Values, error) {
q, err := url.ParseQuery(eparams)
if err != nil {
return nil, err
}
if len(q) != 1 {
return nil, errors.New("len(q) must be 1")
}
for _, v := range q {
dec, err := tya.Decrypt(StringToBytes(v[0]))
if err != nil {
return nil, err
}
return url.ParseQuery(BytesToString(dec))
}
panic("unexpected")
}

122
internal/textio/api_test.go Normal file
View File

@@ -0,0 +1,122 @@
package textio
import (
"encoding/binary"
"net/url"
"testing"
"github.com/fumiama/tienyik"
)
func TestEUrlParams(t *testing.T) {
const aesplain = "moduleCode=DESKTOP_MSGCENTER"
var (
rawkey = []uint32{
2004378729, 1936745065, 1933079672, 1970627951,
842425958, 1932686949, 1903374648, 1936290669,
}
key [32]byte
)
for i, k := range rawkey {
binary.BigEndian.PutUint32(key[i*4:(i+1)*4], k)
}
t.Log(string(key[:])) // wxdispbis8txuueo26ffs2veqs18sism
tya := tienyik.NewAES(key[:])
params := EUrlParams(&tya, url.Values{
"moduleCode": {"DESKTOP_MSGCENTER"},
})
q, err := ParseQuery(&tya, params)
if err != nil {
t.Fatal(err)
}
for k, v := range q {
plainValue := k + "=" + v[0]
if plainValue != aesplain {
t.Fatal("expect", aesplain, "got", plainValue)
}
}
}
func TestEUrlParamsMultiple(t *testing.T) {
var (
rawkey = []uint32{
2004378729, 1936745065, 1933079672, 1970627951,
842425958, 1932686949, 1903374648, 1936290669,
}
key [32]byte
)
for i, k := range rawkey {
binary.BigEndian.PutUint32(key[i*4:(i+1)*4], k)
}
tya := tienyik.NewAES(key[:])
testCases := []struct {
name string
params url.Values
expected map[string]string
}{
{
name: "single parameter",
params: url.Values{
"userId": {"12345"},
},
expected: map[string]string{
"userId": "12345",
},
},
{
name: "multiple parameters",
params: url.Values{
"userId": {"12345"},
"userName": {"testUser"},
"status": {"active"},
},
expected: map[string]string{
"userId": "12345",
"userName": "testUser",
"status": "active",
},
},
{
name: "special characters",
params: url.Values{
"email": {"test@example.com"},
"message": {"Hello World!"},
},
expected: map[string]string{
"email": "test@example.com",
"message": "Hello World!",
},
},
{
name: "chinese characters",
params: url.Values{
"name": {"张三"},
"city": {"北京"},
},
expected: map[string]string{
"name": "张三",
"city": "北京",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
params := EUrlParams(&tya, tc.params)
q, err := ParseQuery(&tya, params)
if err != nil {
t.Fatal(err)
}
for key, expectedValue := range tc.expected {
if vals, ok := q[key]; ok && len(vals) > 0 {
if vals[0] != expectedValue {
t.Fatalf("key %s: expect %s, got %s", key, expectedValue, vals[0])
}
} else {
t.Fatalf("key %s not found in query", key)
}
}
})
}
}

64
internal/textio/name.go Normal file
View File

@@ -0,0 +1,64 @@
package textio
import (
"runtime"
"strings"
)
func Logger(skip int) string {
sb := strings.Builder{}
sb.WriteString("[")
sb.WriteString(FileName(skip + 1))
sb.WriteString("]")
return sb.String()
}
func FileName(skip int) string {
_, file, _, ok := runtime.Caller(skip)
if !ok {
return "unknown"
}
i := strings.LastIndex(file, "/")
if i < 0 {
i = strings.LastIndex(file, "\\")
if i < 0 {
return file
}
}
nm := file[i+1:]
if len(nm) == 0 {
return file
}
i = strings.LastIndex(nm, ".")
if i <= 0 {
return nm
}
return nm[:i]
}
func FuncName(skip int, lowerfirst bool) string {
fn, _, _, ok := runtime.Caller(skip)
if !ok {
panic("cannot get func name of caller")
}
f := runtime.FuncForPC(fn)
if f == nil {
panic("invalid func pc")
}
p := f.Name()
i := strings.LastIndex(p, ".")
if i < 0 {
panic("func name of caller '" + p + " has no '.'")
}
p = p[i+1:]
if len(p) <= 1 {
panic("func name of caller '" + p + " too short'")
}
if lowerfirst {
sb := strings.Builder{}
sb.WriteString(strings.ToLower(p[:1]))
sb.WriteString(p[1:])
return sb.String()
}
return p
}

13
internal/textio/str.go Normal file
View File

@@ -0,0 +1,13 @@
package textio
import "unsafe"
// BytesToString 没有内存开销的转换
func BytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// StringToBytes 没有内存开销的转换
func StringToBytes(s string) (b []byte) {
return unsafe.Slice(unsafe.StringData(s), len(s))
}