github.com/fumiama/NanoBot@v0.0.0-20231122134259-c22d8183efca/helper.go (about)

     1  package nano
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/hex"
     6  	"hash/crc64"
     7  	"net/url"
     8  	"regexp"
     9  	"runtime"
    10  	"strings"
    11  	"unsafe"
    12  )
    13  
    14  func getFuncAndFileNameWithSkip(n int) (string, string) {
    15  	pc, fn, _, ok := runtime.Caller(n)
    16  	if !ok {
    17  		return "", ""
    18  	}
    19  	i := strings.LastIndex(fn, "/") + 1
    20  	if i > 0 {
    21  		fn = strings.TrimSuffix(fn[i:], ".go")
    22  	}
    23  	fullname := runtime.FuncForPC(pc).Name()
    24  	i = strings.LastIndex(fullname, ".") + 1
    25  	if i <= 0 || i >= len(fullname) {
    26  		return fullname, fn
    27  	}
    28  	return fullname[i:], fn
    29  }
    30  
    31  // getThisFuncName 获取正在执行的函数名
    32  func getThisFuncName() string {
    33  	x, _ := getFuncAndFileNameWithSkip(2)
    34  	return x
    35  }
    36  
    37  // getCallerFuncName 获取调用者函数名
    38  func getCallerFuncName() string {
    39  	x, _ := getFuncAndFileNameWithSkip(3)
    40  	return x
    41  }
    42  
    43  // getLogHeader [文件名.函数名]
    44  func getLogHeader() string {
    45  	funcname, filename := getFuncAndFileNameWithSkip(2)
    46  	return "[" + filename + "." + funcname + "]"
    47  }
    48  
    49  // MessageEscape 消息转义
    50  //
    51  // https://bot.q.qq.com/wiki/develop/api/openapi/message/message_format.html
    52  func MessageEscape(text string) string {
    53  	text = strings.ReplaceAll(text, "&", "&amp;")
    54  	text = strings.ReplaceAll(text, "<", "&lt;")
    55  	text = strings.ReplaceAll(text, ">", "&gt;")
    56  	return text
    57  }
    58  
    59  // MessageUnescape 消息解转义
    60  //
    61  // https://bot.q.qq.com/wiki/develop/api/openapi/message/message_format.html
    62  func MessageUnescape(text string) string {
    63  	text = strings.ReplaceAll(text, "&amp;", "&")
    64  	text = strings.ReplaceAll(text, "&lt;", "<")
    65  	text = strings.ReplaceAll(text, "&gt;", ">")
    66  	return text
    67  }
    68  
    69  // HideURL 转义 URL 以避免审核
    70  func HideURL(s string) string {
    71  	s = strings.ReplaceAll(s, ".", "…")
    72  	s = strings.ReplaceAll(s, "http://", "🔗📄:")
    73  	s = strings.ReplaceAll(s, "https://", "🔗🔒:")
    74  	return s
    75  }
    76  
    77  // UnderlineToCamel convert abc_def to AbcDef
    78  func UnderlineToCamel(s string) string {
    79  	sb := strings.Builder{}
    80  	isnextupper := true
    81  	for _, c := range []byte(strings.ToLower(s)) {
    82  		if c == '_' {
    83  			isnextupper = true
    84  			continue
    85  		}
    86  		if isnextupper {
    87  			sb.WriteString(strings.ToUpper(string(c)))
    88  			isnextupper = false
    89  			continue
    90  		}
    91  		sb.WriteByte(c)
    92  	}
    93  	return sb.String()
    94  }
    95  
    96  // resolveURI github.com/wdvxdr1123/ZeroBot/driver/uri.go
    97  func resolveURI(addr string) (network, address string) {
    98  	network, address = "tcp", addr
    99  	uri, err := url.Parse(addr)
   100  	if err == nil && uri.Scheme != "" {
   101  		scheme, ext, _ := strings.Cut(uri.Scheme, "+")
   102  		if ext != "" {
   103  			network = ext
   104  			uri.Scheme = scheme // remove `+unix`/`+tcp4`
   105  			if ext == "unix" {
   106  				uri.Host, uri.Path, _ = strings.Cut(uri.Path, ":")
   107  				uri.Host = base64.StdEncoding.EncodeToString(StringToBytes(uri.Host)) // special handle for unix
   108  			}
   109  			address = uri.String()
   110  		}
   111  	}
   112  	return
   113  }
   114  
   115  // slice is the runtime representation of a slice.
   116  // It cannot be used safely or portably and its representation may
   117  // change in a later release.
   118  //
   119  // Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the
   120  // data it references will not be garbage collected.
   121  type slice struct {
   122  	data unsafe.Pointer
   123  	len  int
   124  	cap  int
   125  }
   126  
   127  // BytesToString 没有内存开销的转换
   128  func BytesToString(b []byte) string {
   129  	return *(*string)(unsafe.Pointer(&b))
   130  }
   131  
   132  // StringToBytes 没有内存开销的转换
   133  func StringToBytes(s string) (b []byte) {
   134  	bh := (*slice)(unsafe.Pointer(&b))
   135  	sh := (*slice)(unsafe.Pointer(&s))
   136  	bh.data = sh.data
   137  	bh.len = sh.len
   138  	bh.cap = sh.len
   139  	return b
   140  }
   141  
   142  // DigestID 归一化 id 为 uint64
   143  func DigestID(id string) uint64 {
   144  	b, err := hex.DecodeString(id)
   145  	if err != nil || len(b) < 8 {
   146  		return 0
   147  	}
   148  	return crc64.Checksum(b, crc64.MakeTable(crc64.ECMA))
   149  }
   150  
   151  const mediafilebed = `https://multimedia.nt.qq.com.cn`
   152  
   153  var mediafileinfourlre = regexp.MustCompile(`/download\?appid=\d+&fileid=[0-9A-Za-z-_]+&rkey=[0-9A-Za-z-_]+`)
   154  
   155  // mediaURL 从 fileinfo 得到 URL
   156  func mediaURL(fileinfo string) (string, error) {
   157  	sb := strings.Builder{}
   158  	data, err := base64.StdEncoding.DecodeString(fileinfo)
   159  	if err != nil {
   160  		return "", err
   161  	}
   162  	sb.WriteString(mediafilebed)
   163  	sb.Write(mediafileinfourlre.Find(data))
   164  	return sb.String(), nil
   165  }