Last active
May 14, 2021 09:43
-
-
Save barryz/c2a82ff6a50d69d101cb443c10dfb3ec to your computer and use it in GitHub Desktop.
A telegram bot that reports traffic usage of Tencent lighthouse.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"fmt" | |
"log" | |
"strconv" | |
"strings" | |
"time" | |
"github.com/pkg/errors" | |
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" | |
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" | |
lighthouse "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/lighthouse/v20200324" | |
"gopkg.in/tucnak/telebot.v2" | |
) | |
const tmpl = `Hi, 您的腾讯云轻量服务器用量如下: | |
实例ID: %s | |
已用流量: %s | |
可用流量: %s | |
套餐流量: %s | |
超出流量: %s | |
套餐起始时间: %s | |
套餐到期时间: %s | |
实例创建时间: %s | |
实例到期时间: %s | |
` | |
const ( | |
ReportHour = 22 | |
ReportMinute = 30 | |
ReportSecond = 00 | |
) | |
func main() { | |
ticker := newTicker() | |
defer ticker.stop() | |
log.Printf("tgbot process start") | |
for { | |
select { | |
case <-ticker.C(): | |
if err := do(); err != nil { | |
log.Printf("ERR: report process failed %s", err.Error()) | |
} | |
log.Printf("INFO: succeeded in processing report") | |
ticker.update() | |
} | |
} | |
} | |
func do() error { | |
ctx, cancel := context.WithCancel(context.Background()) | |
defer cancel() | |
tx, err := txTrafficUsage(ctx) | |
if err != nil { | |
return errors.Wrap(err, "get tx usage failed") | |
} | |
ins, err := txInstanceDesc(ctx) | |
if err != nil { | |
return errors.Wrap(err, "get tx instance failed") | |
} | |
msg := fmt.Sprintf(tmpl, | |
instanceId, | |
byteSize(*tx.TrafficUsed), | |
byteSize(*tx.TrafficPackageRemaining), | |
byteSize(*tx.TrafficPackageTotal), | |
byteSize(*tx.TrafficOverflow), | |
formatTimeString(*tx.StartTime), | |
formatTimeString(*tx.EndTime), | |
formatTimeString(*ins.CreatedTime), | |
formatTimeString(*ins.ExpiredTime), | |
) | |
if err = sendTgMessage(msg); err != nil { | |
return errors.Wrap(err, "send message to tg failed") | |
} | |
return nil | |
} | |
var ( | |
tgBotTotken = "" | |
UserId = 0 | |
) | |
func sendTgMessage(msg string) error { | |
tb, err := telebot.NewBot(telebot.Settings{ | |
Token: tgBotTotken, | |
Updates: 0, | |
Poller: &telebot.LongPoller{ | |
Timeout: 10 * time.Second, | |
}, | |
}) | |
if err != nil { | |
return err | |
} | |
user := &telebot.User{ID: UserId} | |
_, err = tb.Send(user, msg) | |
if err != nil { | |
return err | |
} | |
return nil | |
} | |
var ( | |
credential = common.NewCredential( | |
"foo", | |
"bar", | |
) | |
cpfEndpoint = "lighthouse.tencentcloudapi.com" | |
instanceId = "foobar" | |
region = "ap-hongkong" | |
) | |
func txInstanceDesc(ctx context.Context) (*lighthouse.Instance, error) { | |
cpf := profile.NewClientProfile() | |
cpf.HttpProfile.Endpoint = cpfEndpoint | |
client, _ := lighthouse.NewClient(credential, region, cpf) | |
request := lighthouse.NewDescribeInstancesRequest() | |
request.InstanceIds = []*string{&instanceId} | |
response, err := client.DescribeInstances(request) | |
if err != nil { | |
return nil, err | |
} | |
if len(response.Response.InstanceSet) != 1 { | |
return nil, fmt.Errorf("too many instances desc returned") | |
} | |
return response.Response.InstanceSet[0], nil | |
} | |
func txTrafficUsage(ctx context.Context) (*lighthouse.TrafficPackage, error) { | |
cpf := profile.NewClientProfile() | |
cpf.HttpProfile.Endpoint = cpfEndpoint | |
client, err := lighthouse.NewClient(credential, region, cpf) | |
if err != nil { | |
return nil, err | |
} | |
request := lighthouse.NewDescribeInstancesTrafficPackagesRequest() | |
request.InstanceIds = []*string{&instanceId} | |
response, err := client.DescribeInstancesTrafficPackages(request) | |
if err != nil { | |
return nil, err | |
} | |
if len(response.Response.InstanceTrafficPackageSet) != 1 { | |
return nil, fmt.Errorf("bad instance traffic pkg returned") | |
} | |
if len(response.Response.InstanceTrafficPackageSet[0].TrafficPackageSet) != 1 { | |
return nil, fmt.Errorf("bad instance traffic pkg returned") | |
} | |
traffic := response.Response.InstanceTrafficPackageSet[0].TrafficPackageSet[0] | |
return traffic, nil | |
} | |
const ( | |
BYTE = 1 << (10 * iota) | |
KILOBYTE | |
MEGABYTE | |
GIGABYTE | |
TERABYTE | |
PETABYTE | |
EXABYTE | |
) | |
// byteSize returns a human-readable byte string of the form 10M, 12.5K, and so forth. The following units are available: | |
// E: Exabyte | |
// P: Petabyte | |
// T: Terabyte | |
// G: Gigabyte | |
// M: Megabyte | |
// K: Kilobyte | |
// B: Byte | |
// The unit that results in the smallest number greater than or equal to 1 is always chosen. | |
func byteSize(bytes int64) string { | |
unit := "" | |
value := float64(bytes) | |
switch { | |
case bytes >= EXABYTE: | |
unit = "E" | |
value = value / EXABYTE | |
case bytes >= PETABYTE: | |
unit = "P" | |
value = value / PETABYTE | |
case bytes >= TERABYTE: | |
unit = "T" | |
value = value / TERABYTE | |
case bytes >= GIGABYTE: | |
unit = "G" | |
value = value / GIGABYTE | |
case bytes >= MEGABYTE: | |
unit = "M" | |
value = value / MEGABYTE | |
case bytes >= KILOBYTE: | |
unit = "K" | |
value = value / KILOBYTE | |
case bytes >= BYTE: | |
unit = "B" | |
case bytes == 0: | |
return "0B" | |
} | |
result := strconv.FormatFloat(value, 'f', 1, 64) | |
result = strings.TrimSuffix(result, ".0") | |
return result + unit | |
} | |
var ( | |
shanghaiLoc, _ = time.LoadLocation("Asia/Shanghai") | |
) | |
func formatTimeString(s string) string { | |
t, err := time.ParseInLocation(time.RFC3339, s, shanghaiLoc) | |
if err != nil { | |
panic(err) | |
} | |
layout := "2006-01-02 15:04:05" | |
return t.Format(layout) | |
} | |
type Ticker struct { | |
timer *time.Timer | |
} | |
func newTicker() *Ticker { | |
return &Ticker{timer: time.NewTimer(nextTickDuration())} | |
} | |
func (t *Ticker) update() { | |
t.timer.Reset(nextTickDuration()) | |
} | |
func (t *Ticker) stop() { | |
if t.timer != nil { | |
t.timer.Stop() | |
} | |
} | |
func (t *Ticker) C() <-chan time.Time { | |
if t.timer != nil { | |
return t.timer.C | |
} | |
return make(<-chan time.Time) | |
} | |
func nextTickDuration() time.Duration { | |
now := time.Now() | |
nextTick := time.Date( | |
now.Year(), | |
now.Month(), | |
now.Day(), | |
ReportHour, | |
ReportMinute, | |
ReportSecond, | |
0, | |
shanghaiLoc, | |
) | |
if now.After(nextTick) { | |
nextTick = nextTick.Add(24 * time.Hour) | |
} | |
return nextTick.Sub(now) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment