Browse Source

Initial commit

master
Vladimir Hodakov 2 years ago
commit
3fdcb11dcd
Signed by: fat0troll GPG Key ID: 673980B6882F82C6
71 changed files with 49511 additions and 0 deletions
  1. + 1
    - 0
      .gitignore
  2. + 41
    - 0
      Gopkg.lock
  3. + 34
    - 0
      Gopkg.toml
  4. + 64
    - 0
      context/context.go
  5. + 24
    - 0
      context/exported.go
  6. + 6
    - 0
      example/fw_zookeeper_helper.yaml
  7. + 4
    - 0
      golangci.yaml
  8. + 10
    - 0
      internal/config/struct.go
  9. + 14
    - 0
      internal/config/tdlib.go
  10. + 31
    - 0
      internal/telegram/exported.go
  11. + 121
    - 0
      internal/telegram/telegram.go
  12. + 43
    - 0
      main.go
  13. + 29
    - 0
      vendor/github.com/Arman92/go-tdlib/.gitignore
  14. + 124
    - 0
      vendor/github.com/Arman92/go-tdlib/README.md
  15. + 7669
    - 0
      vendor/github.com/Arman92/go-tdlib/methods.go
  16. + 349
    - 0
      vendor/github.com/Arman92/go-tdlib/tdlib.go
  17. + 25335
    - 0
      vendor/github.com/Arman92/go-tdlib/types.go
  18. + 25
    - 0
      vendor/github.com/rs/zerolog/.gitignore
  19. + 13
    - 0
      vendor/github.com/rs/zerolog/.travis.yml
  20. + 1
    - 0
      vendor/github.com/rs/zerolog/CNAME
  21. + 21
    - 0
      vendor/github.com/rs/zerolog/LICENSE
  22. + 591
    - 0
      vendor/github.com/rs/zerolog/README.md
  23. + 1
    - 0
      vendor/github.com/rs/zerolog/_config.yml
  24. + 224
    - 0
      vendor/github.com/rs/zerolog/array.go
  25. + 369
    - 0
      vendor/github.com/rs/zerolog/console.go
  26. + 381
    - 0
      vendor/github.com/rs/zerolog/context.go
  27. + 47
    - 0
      vendor/github.com/rs/zerolog/ctx.go
  28. + 56
    - 0
      vendor/github.com/rs/zerolog/encoder.go
  29. + 35
    - 0
      vendor/github.com/rs/zerolog/encoder_cbor.go
  30. + 32
    - 0
      vendor/github.com/rs/zerolog/encoder_json.go
  31. + 677
    - 0
      vendor/github.com/rs/zerolog/event.go
  32. + 242
    - 0
      vendor/github.com/rs/zerolog/fields.go
  33. + 76
    - 0
      vendor/github.com/rs/zerolog/globals.go
  34. + 1
    - 0
      vendor/github.com/rs/zerolog/go.mod
  35. + 60
    - 0
      vendor/github.com/rs/zerolog/hook.go
  36. + 56
    - 0
      vendor/github.com/rs/zerolog/internal/cbor/README.md
  37. + 11
    - 0
      vendor/github.com/rs/zerolog/internal/cbor/base.go
  38. + 100
    - 0
      vendor/github.com/rs/zerolog/internal/cbor/cbor.go
  39. + 614
    - 0
      vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go
  40. + 68
    - 0
      vendor/github.com/rs/zerolog/internal/cbor/string.go
  41. + 93
    - 0
      vendor/github.com/rs/zerolog/internal/cbor/time.go
  42. + 478
    - 0
      vendor/github.com/rs/zerolog/internal/cbor/types.go
  43. + 12
    - 0
      vendor/github.com/rs/zerolog/internal/json/base.go
  44. + 85
    - 0
      vendor/github.com/rs/zerolog/internal/json/bytes.go
  45. + 121
    - 0
      vendor/github.com/rs/zerolog/internal/json/string.go
  46. + 76
    - 0
      vendor/github.com/rs/zerolog/internal/json/time.go
  47. + 402
    - 0
      vendor/github.com/rs/zerolog/internal/json/types.go
  48. + 400
    - 0
      vendor/github.com/rs/zerolog/log.go
  49. BIN
      vendor/github.com/rs/zerolog/pretty.png
  50. + 126
    - 0
      vendor/github.com/rs/zerolog/sampler.go
  51. + 57
    - 0
      vendor/github.com/rs/zerolog/syslog.go
  52. + 100
    - 0
      vendor/github.com/rs/zerolog/writer.go
  53. + 12
    - 0
      vendor/gopkg.in/yaml.v2/.travis.yml
  54. + 201
    - 0
      vendor/gopkg.in/yaml.v2/LICENSE
  55. + 31
    - 0
      vendor/gopkg.in/yaml.v2/LICENSE.libyaml
  56. + 13
    - 0
      vendor/gopkg.in/yaml.v2/NOTICE
  57. + 133
    - 0
      vendor/gopkg.in/yaml.v2/README.md
  58. + 739
    - 0
      vendor/gopkg.in/yaml.v2/apic.go
  59. + 775
    - 0
      vendor/gopkg.in/yaml.v2/decode.go
  60. + 1685
    - 0
      vendor/gopkg.in/yaml.v2/emitterc.go
  61. + 390
    - 0
      vendor/gopkg.in/yaml.v2/encode.go
  62. + 5
    - 0
      vendor/gopkg.in/yaml.v2/go.mod
  63. + 1095
    - 0
      vendor/gopkg.in/yaml.v2/parserc.go
  64. + 412
    - 0
      vendor/gopkg.in/yaml.v2/readerc.go
  65. + 258
    - 0
      vendor/gopkg.in/yaml.v2/resolve.go
  66. + 2696
    - 0
      vendor/gopkg.in/yaml.v2/scannerc.go
  67. + 113
    - 0
      vendor/gopkg.in/yaml.v2/sorter.go
  68. + 26
    - 0
      vendor/gopkg.in/yaml.v2/writerc.go
  69. + 466
    - 0
      vendor/gopkg.in/yaml.v2/yaml.go
  70. + 738
    - 0
      vendor/gopkg.in/yaml.v2/yamlh.go
  71. + 173
    - 0
      vendor/gopkg.in/yaml.v2/yamlprivateh.go

+ 1
- 0
.gitignore

@ -0,0 +1 @@
fw_zookeeper_helper.yaml

+ 41
- 0
Gopkg.lock

@ -0,0 +1,41 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
digest = "1:cdfaade39c21052a144a32f944ce80030cbdce760d0c63c60ca8b9f59928a6f4"
name = "github.com/Arman92/go-tdlib"
packages = ["."]
pruneopts = "UT"
revision = "9577ff528640031a5c3a5f7386c896255f69c723"
[[projects]]
digest = "1:6112a5eaec2ec65df289ccbb7a730aaf03e3c5cce6c906d367ccf9b7ac567604"
name = "github.com/rs/zerolog"
packages = [
".",
"internal/cbor",
"internal/json",
]
pruneopts = "UT"
revision = "8747b7b3a51b5d08ee7ac50eaf4869edaf9f714a"
version = "v1.11.0"
[[projects]]
digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
version = "v2.2.2"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/Arman92/go-tdlib",
"github.com/rs/zerolog",
"gopkg.in/yaml.v2",
]
solver-name = "gps-cdcl"
solver-version = 1

+ 34
- 0
Gopkg.toml

@ -0,0 +1,34 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/Arman92/go-tdlib"
[prune]
go-tests = true
unused-packages = true

+ 64
- 0
context/context.go

@ -0,0 +1,64 @@
// Fantasy World Zookeeper Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package context
import (
"fmt"
"github.com/rs/zerolog"
"gopkg.in/yaml.v2"
"io/ioutil"
"lab.wtfteam.pro/fat0troll/fw_zookeeper_helper/internal/config"
"os"
"path/filepath"
"runtime"
)
// getMemoryUsage returns memory usage for logger.
func (c *Context) getMemoryUsage(e *zerolog.Event, level zerolog.Level, message string) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
e.Str("memalloc", fmt.Sprintf("%dMB", m.Alloc/1024/1024))
e.Str("memsys", fmt.Sprintf("%dMB", m.Sys/1024/1024))
e.Str("numgc", fmt.Sprintf("%d", m.NumGC))
}
// Init is an initialization function for core context
// Without these parts of the application we can't start at all
func (c *Context) Init() {
c.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()
c.Logger = c.Logger.Hook(zerolog.HookFunc(c.getMemoryUsage))
c.Logger.Info().Msgf("fw_zookeeper_helper v. %s is starting...", VERSION)
}
// InitConfiguration reads configuration from YAML and parses it in
// config.Struct.
func (c *Context) InitConfiguration() bool {
c.Logger.Info().Msg("Loading configuration files...")
configPath := os.Getenv("BOT_CONFIG")
if configPath == "" {
configPath = "./example/fw_zookeeper_helper.yaml"
}
normalizedConfigPath, _ := filepath.Abs(configPath)
c.Logger.Debug().Msgf("Configuration file path: %s", normalizedConfigPath)
// Read configuration file into []byte.
fileData, err := ioutil.ReadFile(normalizedConfigPath)
if err != nil {
c.Logger.Error().Err(err).Msg("Failed to read configuration file")
return false
}
c.Config = &config.Struct{}
err = yaml.Unmarshal(fileData, c.Config)
if err != nil {
c.Logger.Error().Err(err).Msg("Failed to parse configuration file")
return false
}
c.Logger.Info().Msg("Configuration file parsed successfully")
return true
}

+ 24
- 0
context/exported.go

@ -0,0 +1,24 @@
// Fantasy World Zookeeper Helper Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package context
import (
"github.com/rs/zerolog"
"lab.wtfteam.pro/fat0troll/fw_zookeeper_helper/internal/config"
)
// VERSION is the current bot's version
const VERSION = "0.0.1"
// Context is the main application context.
type Context struct {
Config *config.Struct
Logger zerolog.Logger
}
// NewContext is an initialization function for Context
func NewContext() *Context {
c := &Context{}
return c
}

+ 6
- 0
example/fw_zookeeper_helper.yaml

@ -0,0 +1,6 @@
tdlib:
api_id: "your-id"
api_hash: "your-hash"
database_directory: "/tmp/tdlib-db/"
files_directory: "/tmp/tdlib-files/"
errors_file: "/tmp/tdlib.txt"

+ 4
- 0
golangci.yaml

@ -0,0 +1,4 @@
run:
deadline: 5m
linters:
enable-all: true

+ 10
- 0
internal/config/struct.go

@ -0,0 +1,10 @@
// Fantasy World Zookeeper Helper Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package config
// Struct is a main configuration structure that holds all other
// structs within.
type Struct struct {
TDLib TDLib `yaml:"tdlib"`
}

+ 14
- 0
internal/config/tdlib.go

@ -0,0 +1,14 @@
// Fantasy World Zookeeper Helper Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package config
// TDLib struct handles all configuration of tdlib
// tdlib is an official Telegram MTProto library
type TDLib struct {
APIID string `yaml:"api_id"`
APIHash string `yaml:"api_hash"`
DatabaseDirectory string `yaml:"database_directory"`
FilesDirectory string `yaml:"files_directory"`
ErrorsFile string `yaml:"errors_file"`
}

+ 31
- 0
internal/telegram/exported.go

@ -0,0 +1,31 @@
// Fantasy World Zookeeper Helper Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package telegram
import (
tdlib "github.com/Arman92/go-tdlib"
"github.com/rs/zerolog"
"lab.wtfteam.pro/fat0troll/fw_zookeeper_helper/context"
)
var (
c *context.Context
log zerolog.Logger
client *tdlib.Client
)
// New initializes package
func New(cc *context.Context) {
c = cc
log = c.Logger.With().Str("domain", "telegram").Int("version", 1).Logger()
log.Info().Msg("Starting Telegram MTProto instance")
Authenticate()
go func() {
Connect()
}()
}

+ 121
- 0
internal/telegram/telegram.go

@ -0,0 +1,121 @@
// Fantasy World Zookeeper Helper Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package telegram
import (
"fmt"
tdlib "github.com/Arman92/go-tdlib"
"lab.wtfteam.pro/fat0troll/fw_zookeeper_helper/context"
"strings"
)
// Authenticate connects instance to Telegram
// At first launch we need to login manually - there is no way to automate
// Telegram login
func Authenticate() {
tdlib.SetLogVerbosityLevel(1)
tdlib.SetFilePath(c.Config.TDLib.ErrorsFile)
// Create new instance of client
client = tdlib.NewClient(tdlib.Config{
APIID: c.Config.TDLib.APIID,
APIHash: c.Config.TDLib.APIHash,
SystemLanguageCode: "en",
DeviceModel: "fw_zookeeper_helper",
SystemVersion: context.VERSION,
ApplicationVersion: context.VERSION,
UseMessageDatabase: true,
UseFileDatabase: true,
UseChatInfoDatabase: true,
UseTestDataCenter: false,
DatabaseDirectory: c.Config.TDLib.DatabaseDirectory,
FileDirectory: c.Config.TDLib.FilesDirectory,
IgnoreFileNames: false,
})
for {
currentState, _ := client.Authorize()
log.Info().Msg("Starting Telegram authorization...")
if currentState.GetAuthorizationStateEnum() == tdlib.AuthorizationStateWaitPhoneNumberType {
fmt.Print("Enter phone: ")
var number string
fmt.Scanln(&number)
_, err := client.SendPhoneNumber(number)
if err != nil {
log.Error().Err(err).Msg("Error sending phone number")
}
} else if currentState.GetAuthorizationStateEnum() == tdlib.AuthorizationStateWaitCodeType {
fmt.Print("Enter code: ")
var code string
fmt.Scanln(&code)
_, err := client.SendAuthCode(code)
if err != nil {
log.Error().Err(err).Msg("Error sending auth code")
}
} else if currentState.GetAuthorizationStateEnum() == tdlib.AuthorizationStateWaitPasswordType {
fmt.Print("Enter Password: ")
var password string
fmt.Scanln(&password)
_, err := client.SendAuthPassword(password)
if err != nil {
log.Error().Err(err).Msg("Error sending auth password")
}
} else if currentState.GetAuthorizationStateEnum() == tdlib.AuthorizationStateReadyType {
me, _ := client.GetMe()
log.Info().Msgf("Logged into Telegram as @%s", me.Username)
break
}
}
}
// Connect connects into updates chain
//
func Connect() {
go func() {
// Create an filter function which will be used to filter out unwanted tdlib messages
eventFilter := func(msg *tdlib.TdMessage) bool {
updateMsg := (*msg).(*tdlib.UpdateNewMessage)
// We need only messages, created by @FWorldBot
return updateMsg.Message.ViaBotUserID == 6.74929718e+08
}
// Here we can add a receiver to retreive any message type we want
// We like to get UpdateNewMessage events and with a specific FilterFunc
receiver := client.AddEventReceiver(&tdlib.UpdateNewMessage{}, eventFilter, 5)
for newMsg := range receiver.Chan {
updateMsg := (newMsg).(*tdlib.UpdateNewMessage)
// Check if message text contains needed battle data
msgText := updateMsg.Message.Content.(*tdlib.MessageText)
if strings.HasPrefix(msgText.Text.Text, "Я встретил") {
battleType := ""
battleTag := ""
if strings.Contains(msgText.Text.Text, "Огров") {
battleType = "Огры!"
}
if strings.Contains(msgText.Text.Text, "Буйволов") {
battleType = "Буйволы!"
}
if strings.Contains(msgText.Text.Text, "Кабанов") {
battleType = "Кабаны!"
}
keyboard := updateMsg.Message.ReplyMarkup.(*tdlib.ReplyMarkupInlineKeyboard)
if len(keyboard.Rows) > 0 {
if len(keyboard.Rows[0]) > 0 {
button := keyboard.Rows[0][0]
buttonQuery := button.Type.(*tdlib.InlineKeyboardButtonTypeCallback)
battleTag = string(buttonQuery.Data)
}
}
log.Debug().Msgf("Battle type: %s", battleType)
log.Debug().Msgf("Battle type: %s", battleTag)
}
}
}()
rawUpdates := client.GetRawUpdatesChannel(100)
for update := range rawUpdates {
log.Debug().Msgf("Update of type %s received", update.Data["@type"])
}
}

+ 43
- 0
main.go

@ -0,0 +1,43 @@
// Fantasy World Zookeeper Helper Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package main
import (
"lab.wtfteam.pro/fat0troll/fw_zookeeper_helper/context"
"lab.wtfteam.pro/fat0troll/fw_zookeeper_helper/internal/telegram"
"os"
"os/signal"
"runtime"
"syscall"
)
func main() {
// Before any real work - lock to OS thread. We shouldn't leave it until
// shutdown
runtime.LockOSThread()
// Initializing context
c := context.NewContext()
c.Init()
c.InitConfiguration()
telegram.New(c)
// CTRL+C handler.
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt)
shutdownDone := make(chan bool, 1)
go func() {
signalThing := <-interrupt
if signalThing == syscall.SIGTERM || signalThing == syscall.SIGINT {
c.Logger.Info().Msg("Got " + signalThing.String() + " signal, shutting down...")
shutdownDone <- true
}
}()
<-shutdownDone
os.Exit(0)
}

+ 29
- 0
vendor/github.com/Arman92/go-tdlib/.gitignore

@ -0,0 +1,29 @@
# Any file without an extension
*
!*/
!*.*
/.vscode
/.git_old
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
tdlib-db
tdlib-files
errors.txt

+ 124
- 0
vendor/github.com/Arman92/go-tdlib/README.md

@ -0,0 +1,124 @@
# go-tdlib
Golang Telegram TdLib JSON bindings
## Introduction
Telegram Tdlib is a complete library for creating telegram clients, it laso has a simple tdjson ready-to-use library to ease
the integration with different programming languages and platforms.
**go-tdlib** is a complete tdlib-tdjson binding package to help you create your own Telegram clients.
**NOTE:** basic tdjson-golang binding is inspired from this package: [go-tdjson](https://github.com/L11R/go-tdjson)
All the classes and functions declared in [Tdlib TypeLanguage schema](https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl)
file have been exported using the autogenerate tool [tl-parser](https://github.com/Arman92/go-tl-parser).
So you can use every single type and method in Tdlib.
## Key features:
* Autogenerated golang structs and methods of tdlib .tl schema
* Custom event receivers defined by user (e.g. get only text messages from a specific user)
* Supports all tdjson functions: Send(), Execute(), Receive(), Destroy(), SetFilePath(), SetLogVerbosityLevel()
* Supports all tdlib functions and types
## Installation
First of all you need to clone the Tdlib repo and build it:
```bash
git clone git@github.com:tdlib/td.git
cd td
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build . -- -j5
make install
# -j5 refers to number of your cpu cores + 1 for multi-threaded build.
```
If hit any build errors, refer to [Tdlib build instructions](https://github.com/tdlib/td#building)
I'm using static linking against tdlib so it won't require to build the whole tdlib source files.
## Docker
You can use prebuilt tdlib with following Docker image:
***Windows:***
``` shell
docker pull mihaildemidoff/tdlib-go
```
## Example
Here is a simple example for authorization and fetching updates:
```golang
package main
import (
"fmt"
"github.com/Arman92/go-tdlib"
)
func main() {
tdlib.SetLogVerbosityLevel(1)
tdlib.SetFilePath("./errors.txt")
// Create new instance of client
client := tdlib.NewClient(tdlib.Config{
APIID: "187786",
APIHash: "e782045df67ba48e441ccb105da8fc85",
SystemLanguageCode: "en",
DeviceModel: "Server",
SystemVersion: "1.0.0",
ApplicationVersion: "1.0.0",
UseMessageDatabase: true,
UseFileDatabase: true,
UseChatInfoDatabase: true,
UseTestDataCenter: false,
DatabaseDirectory: "./tdlib-db",
FileDirectory: "./tdlib-files",
IgnoreFileNames: false,
})
for {
currentState, _ := client.Authorize()
if currentState.GetAuthorizationStateEnum() == tdlib.AuthorizationStateWaitPhoneNumberType {
fmt.Print("Enter phone: ")
var number string
fmt.Scanln(&number)
_, err := client.SendPhoneNumber(number)
if err != nil {
fmt.Printf("Error sending phone number: %v", err)
}
} else if currentState.GetAuthorizationStateEnum() == tdlib.AuthorizationStateWaitCodeType {
fmt.Print("Enter code: ")
var code string
fmt.Scanln(&code)
_, err := client.SendAuthCode(code)
if err != nil {
fmt.Printf("Error sending auth code : %v", err)
}
} else if currentState.GetAuthorizationStateEnum() == tdlib.AuthorizationStateWaitPasswordType {
fmt.Print("Enter Password: ")
var password string
fmt.Scanln(&password)
_, err := client.SendAuthPassword(password)
if err != nil {
fmt.Printf("Error sending auth password: %v", err)
}
} else if currentState.GetAuthorizationStateEnum() == tdlib.AuthorizationStateReadyType {
fmt.Println("Authorization Ready! Let's rock")
break
}
}
// Main loop
for update := range client.RawUpdates {
// Show all updates
fmt.Println(update.Data)
fmt.Print("\n\n")
}
}
```
More examples can be found on [examples folder](https://github.com/Arman92/go-tdlib/tree/master/examples)

+ 7669
- 0
vendor/github.com/Arman92/go-tdlib/methods.go
File diff suppressed because it is too large
View File


+ 349
- 0
vendor/github.com/Arman92/go-tdlib/tdlib.go

@ -0,0 +1,349 @@
package tdlib
//#cgo linux CFLAGS: -I/usr/local/include
//#cgo darwin CFLAGS: -I/usr/local/include
//#cgo windows CFLAGS: -IC:/src/td -IC:/src/td/build
//#cgo linux LDFLAGS: -L/usr/local/lib -ltdjson_static -ltdjson_private -ltdclient -ltdcore -ltdactor -ltddb -ltdsqlite -ltdnet -ltdutils -lstdc++ -lssl -lcrypto -ldl -lz -lm
//#cgo darwin LDFLAGS: -L/usr/local/lib -L/usr/local/opt/openssl/lib -ltdjson_static -ltdjson_private -ltdclient -ltdcore -ltdactor -ltddb -ltdsqlite -ltdnet -ltdutils -lstdc++ -lssl -lcrypto -ldl -lz -lm
//#cgo windows LDFLAGS: -LC:/src/td/build/Debug -ltdjson
//#include <stdlib.h>
//#include <td/telegram/td_json_client.h>
//#include <td/telegram/td_log.h>
import "C"
import (
"encoding/json"
"errors"
"fmt"
"math/rand"
"reflect"
"sync"
"time"
"unsafe"
)
// UpdateData alias for use in UpdateMsg
type UpdateData map[string]interface{}
// UpdateMsg is used to unmarshal recieved json strings into
type UpdateMsg struct {
Data UpdateData
Raw []byte
}
// EventFilterFunc used to filter out unwanted messages in receiver channels
type EventFilterFunc func(msg *TdMessage) bool
// EventReceiver used to retreive updates from tdlib to user
type EventReceiver struct {
Instance TdMessage
Chan chan TdMessage
FilterFunc EventFilterFunc
}
// Client is the Telegram TdLib client
type Client struct {
Client unsafe.Pointer
Config Config
rawUpdates chan UpdateMsg
receivers []EventReceiver
waiters sync.Map
receiverLock *sync.Mutex
}
// Config holds tdlibParameters
type Config struct {
APIID string // Application identifier for Telegram API access, which can be obtained at https://my.telegram.org --- must be non-empty..
APIHash string // Application identifier hash for Telegram API access, which can be obtained at https://my.telegram.org --- must be non-empty..
SystemLanguageCode string // IETF language tag of the user's operating system language; must be non-empty.
DeviceModel string // Model of the device the application is being run on; must be non-empty.
SystemVersion string // Version of the operating system the application is being run on; must be non-empty.
ApplicationVersion string // Application version; must be non-empty.
// Optional fields
UseTestDataCenter bool // if set to true, the Telegram test environment will be used instead of the production environment.
DatabaseDirectory string // The path to the directory for the persistent database; if empty, the current working directory will be used.
FileDirectory string // The path to the directory for storing files; if empty, database_directory will be used.
UseFileDatabase bool // If set to true, information about downloaded and uploaded files will be saved between application restarts.
UseChatInfoDatabase bool // If set to true, the library will maintain a cache of users, basic groups, supergroups, channels and secret chats. Implies use_file_database.
UseMessageDatabase bool // If set to true, the library will maintain a cache of chats and messages. Implies use_chat_info_database.
UseSecretChats bool // If set to true, support for secret chats will be enabled.
EnableStorageOptimizer bool // If set to true, old files will automatically be deleted.
IgnoreFileNames bool // If set to true, original file names will be ignored. Otherwise, downloaded files will be saved under names as close as possible to the original name.
}
// NewClient Creates a new instance of TDLib.
// Has two public fields:
// Client itself and RawUpdates channel
func NewClient(config Config) *Client {
// Seed rand with time
rand.Seed(time.Now().UnixNano())
client := Client{Client: C.td_json_client_create()}
client.receivers = make([]EventReceiver, 0, 1)
client.receiverLock = &sync.Mutex{}
client.Config = config
go func() {
for {
// get update
updateBytes := client.Receive(10)
var updateData UpdateData
json.Unmarshal(updateBytes, &updateData)
// does new update has @extra field?
if extra, hasExtra := updateData["@extra"].(string); hasExtra {
// trying to load update with this salt
if waiter, found := client.waiters.Load(extra); found {
// found? send it to waiter channel
waiter.(chan UpdateMsg) <- UpdateMsg{Data: updateData, Raw: updateBytes}
// trying to prevent memory leak
close(waiter.(chan UpdateMsg))
}
} else {
// does new updates has @type field?
if msgType, hasType := updateData["@type"]; hasType {
if client.rawUpdates != nil {
// if rawUpdates is initialized, send the update in rawUpdates channel
client.rawUpdates <- UpdateMsg{Data: updateData, Raw: updateBytes}
}
client.receiverLock.Lock()
for _, receiver := range client.receivers {
if msgType == receiver.Instance.MessageType() {
var newMsg TdMessage
newMsg = reflect.New(reflect.ValueOf(receiver.Instance).Elem().Type()).Interface().(TdMessage)
err := json.Unmarshal(updateBytes, &newMsg)
if err != nil {
fmt.Printf("Error unmarhaling to type %v", err)
}
if receiver.FilterFunc(&newMsg) {
receiver.Chan <- newMsg
}
}
}
client.receiverLock.Unlock()
}
}
}
}()
return &client
}
// GetRawUpdatesChannel creates a general channel that fetches every update comming from tdlib
func (client *Client) GetRawUpdatesChannel(capacity int) chan UpdateMsg {
client.rawUpdates = make(chan UpdateMsg, capacity)
return client.rawUpdates
}
// AddEventReceiver adds a new receiver to be subscribed in receiver channels
func (client *Client) AddEventReceiver(msgInstance TdMessage, filterFunc EventFilterFunc, channelCapacity int) EventReceiver {
receiver := EventReceiver{
Instance: msgInstance,
Chan: make(chan TdMessage, channelCapacity),
FilterFunc: filterFunc,
}
client.receiverLock.Lock()
defer client.receiverLock.Unlock()
client.receivers = append(client.receivers, receiver)
return receiver
}
// DestroyInstance Destroys the TDLib client instance.
// After this is called the client instance shouldn't be used anymore.
func (client *Client) DestroyInstance() {
C.td_json_client_destroy(client.Client)
}
// Send Sends request to the TDLib client.
// You can provide string or UpdateData.
func (client *Client) Send(jsonQuery interface{}) {
var query *C.char
switch jsonQuery.(type) {
case string:
query = C.CString(jsonQuery.(string))
case UpdateData:
jsonBytes, _ := json.Marshal(jsonQuery.(UpdateData))
query = C.CString(string(jsonBytes))
}
defer C.free(unsafe.Pointer(query))
C.td_json_client_send(client.Client, query)
}
// Receive Receives incoming updates and request responses from the TDLib client.
// You can provide string or UpdateData.
func (client *Client) Receive(timeout float64) []byte {
result := C.td_json_client_receive(client.Client, C.double(timeout))
return []byte(C.GoString(result))
}
// Execute Synchronously executes TDLib request.
// Only a few requests can be executed synchronously.
func (client *Client) Execute(jsonQuery interface{}) UpdateMsg {
var query *C.char
switch jsonQuery.(type) {
case string:
query = C.CString(jsonQuery.(string))
case UpdateData:
jsonBytes, _ := json.Marshal(jsonQuery.(UpdateData))
query = C.CString(string(jsonBytes))
}
defer C.free(unsafe.Pointer(query))
result := C.td_json_client_execute(client.Client, query)
var update UpdateData
json.Unmarshal([]byte(C.GoString(result)), &update)
return UpdateMsg{Data: update, Raw: []byte(C.GoString(result))}
}
// SetFilePath Sets the path to the file to where the internal TDLib log will be written.
// By default TDLib writes logs to stderr or an OS specific log.
// Use this method to write the log to a file instead.
func SetFilePath(path string) {
query := C.CString(path)
defer C.free(unsafe.Pointer(query))
C.td_set_log_file_path(query)
}
// SetLogVerbosityLevel Sets the verbosity level of the internal logging of TDLib.
// By default the TDLib uses a verbosity level of 5 for logging.
func SetLogVerbosityLevel(level int) {
C.td_set_log_verbosity_level(C.int(level))
}
// SendAndCatch Sends request to the TDLib client and catches the result in updates channel.
// You can provide string or UpdateData.
func (client *Client) SendAndCatch(jsonQuery interface{}) (UpdateMsg, error) {
var update UpdateData
switch jsonQuery.(type) {
case string:
// unmarshal JSON into map, we don't have @extra field, if user don't set it
json.Unmarshal([]byte(jsonQuery.(string)), &update)
case UpdateData:
update = jsonQuery.(UpdateData)
}
// letters for generating random string
letterBytes := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// generate random string for @extra field
b := make([]byte, 32)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
randomString := string(b)
// set @extra field
update["@extra"] = randomString
// create waiter chan and save it in Waiters
waiter := make(chan UpdateMsg, 1)
client.waiters.Store(randomString, waiter)
// send it through already implemented method
client.Send(update)
select {
// wait response from main loop in NewClient()
case response := <-waiter:
return response, nil
// or timeout
case <-time.After(10 * time.Second):
client.waiters.Delete(randomString)
return UpdateMsg{}, errors.New("timeout")
}
}
// Authorize is used to authorize the users
func (client *Client) Authorize() (AuthorizationState, error) {
state, err := client.GetAuthorizationState()
if err != nil {
return nil, err
}
if state.GetAuthorizationStateEnum() == AuthorizationStateWaitEncryptionKeyType {
ok, err := client.CheckDatabaseEncryptionKey(nil)
if ok == nil || err != nil {
return nil, err
}
} else if state.GetAuthorizationStateEnum() == AuthorizationStateWaitTdlibParametersType {
client.sendTdLibParams()
}
authState, err := client.GetAuthorizationState()
return authState, err
}
func (client *Client) sendTdLibParams() {
client.Send(UpdateData{
"@type": "setTdlibParameters",
"parameters": UpdateData{
"@type": "tdlibParameters",
"use_test_dc": client.Config.UseTestDataCenter,
"database_directory": client.Config.DatabaseDirectory,
"files_directory": client.Config.FileDirectory,
"use_file_database": client.Config.UseFileDatabase,
"use_chat_info_database": client.Config.UseChatInfoDatabase,
"use_message_database": client.Config.UseMessageDatabase,
"use_secret_chats": client.Config.UseSecretChats,
"api_id": client.Config.APIID,
"api_hash": client.Config.APIHash,
"system_language_code": client.Config.SystemLanguageCode,
"device_model": client.Config.DeviceModel,
"system_version": client.Config.SystemVersion,
"application_version": client.Config.ApplicationVersion,
"enable_storage_optimizer": client.Config.EnableStorageOptimizer,
"ignore_file_names": client.Config.IgnoreFileNames,
},
})
}
// SendPhoneNumber sends phone number to tdlib
func (client *Client) SendPhoneNumber(phoneNumber string) (AuthorizationState, error) {
_, err := client.SetAuthenticationPhoneNumber(phoneNumber, false, false)
if err != nil {
return nil, err
}
authState, err := client.GetAuthorizationState()
return authState, err
}
// SendAuthCode sends auth code to tdlib
func (client *Client) SendAuthCode(code string) (AuthorizationState, error) {
_, err := client.CheckAuthenticationCode(code, "", "")
if err != nil {
return nil, err
}
authState, err := client.GetAuthorizationState()
return authState, err
}
// SendAuthPassword sends two-step verification password (user defined)to tdlib
func (client *Client) SendAuthPassword(password string) (AuthorizationState, error) {
_, err := client.CheckAuthenticationPassword(password)
if err != nil {
return nil, err
}
authState, err := client.GetAuthorizationState()
return authState, err
}

+ 25335
- 0
vendor/github.com/Arman92/go-tdlib/types.go
File diff suppressed because it is too large
View File


+ 25
- 0
vendor/github.com/rs/zerolog/.gitignore

@ -0,0 +1,25 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
tmp
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

+ 13
- 0
vendor/github.com/rs/zerolog/.travis.yml

@ -0,0 +1,13 @@
language: go
go:
- "1.7"
- "1.8"
- "1.9"
- "1.10"
- "master"
matrix:
allow_failures:
- go: "master"
script:
- go test -v -race -cpu=1,2,4 -bench . -benchmem ./...
- go test -v -tags binary_log -race -cpu=1,2,4 -bench . -benchmem ./...

+ 1
- 0
vendor/github.com/rs/zerolog/CNAME

@ -0,0 +1 @@
zerolog.io

+ 21
- 0
vendor/github.com/rs/zerolog/LICENSE

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Olivier Poitrey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 591
- 0
vendor/github.com/rs/zerolog/README.md

@ -0,0 +1,591 @@
# Zero Allocation JSON Logger
[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/zerolog) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/zerolog/master/LICENSE) [![Build Status](https://travis-ci.org/rs/zerolog.svg?branch=master)](https://travis-ci.org/rs/zerolog) [![Coverage](http://gocover.io/_badge/github.com/rs/zerolog)](http://gocover.io/github.com/rs/zerolog)
The zerolog package provides a fast and simple logger dedicated to JSON output.
Zerolog's API is designed to provide both a great developer experience and stunning [performance](#benchmarks). Its unique chaining API allows zerolog to write JSON (or CBOR) log events by avoiding allocations and reflection.
Uber's [zap](https://godoc.org/go.uber.org/zap) library pioneered this approach. Zerolog is taking this concept to the next level with a simpler to use API and even better performance.
To keep the code base and the API simple, zerolog focuses on efficient structured logging only. Pretty logging on the console is made possible using the provided (but inefficient) [`zerolog.ConsoleWriter`](#pretty-logging).
![Pretty Logging Image](pretty.png)
## Who uses zerolog
Find out [who uses zerolog](https://github.com/rs/zerolog/wiki/Who-uses-zerolog) and add your company / project to the list.
## Features
* Blazing fast
* Low to zero allocation
* Level logging
* Sampling
* Hooks
* Contextual fields
* `context.Context` integration
* `net/http` helpers
* JSON and CBOR encoding formats
* Pretty logging for development
## Installation
```go
go get -u github.com/rs/zerolog/log
```
## Getting Started
### Simple Logging Example
For simple logging, import the global logger package **github.com/rs/zerolog/log**
```go
package main
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
// UNIX Time is faster and smaller than most timestamps
// If you set zerolog.TimeFieldFormat to an empty string,
// logs will write with UNIX time
zerolog.TimeFieldFormat = ""
log.Print("hello world")
}
// Output: {"time":1516134303,"level":"debug","message":"hello world"}
```
> Note: By default log writes to `os.Stderr`
> Note: The default log level for `log.Print` is *debug*
### Contextual Logging
**zerolog** allows data to be added to log messages in the form of key:value pairs. The data added to the message adds "context" about the log event that can be critical for debugging as well as myriad other purposes. An example of this is below:
```go
package main
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
zerolog.TimeFieldFormat = ""
log.Debug().
Str("Scale", "833 cents").
Float64("Interval", 833.09).
Msg("Fibonacci is everywhere")
}
// Output: {"time":1524104936,"level":"debug","Scale":"833 cents","Interval":833.09,"message":"Fibonacci is everywhere"}
```
> You'll note in the above example that when adding contextual fields, the fields are strongly typed. You can find the full list of supported fields [here](#standard-types)
### Leveled Logging
#### Simple Leveled Logging Example
```go
package main
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
zerolog.TimeFieldFormat = ""
log.Info().Msg("hello world")
}
// Output: {"time":1516134303,"level":"info","message":"hello world"}
```
> It is very important to note that when using the **zerolog** chaining API, as shown above (`log.Info().Msg("hello world"`), the chain must have either the `Msg` or `Msgf` method call. If you forget to add either of these, the log will not occur and there is no compile time error to alert you of this.
**zerolog** allows for logging at the following levels (from highest to lowest):
* panic (`zerolog.PanicLevel`, 5)
* fatal (`zerolog.FatalLevel`, 4)
* error (`zerolog.ErrorLevel`, 3)
* warn (`zerolog.WarnLevel`, 2)
* info (`zerolog.InfoLevel`, 1)
* debug (`zerolog.DebugLevel`, 0)
You can set the Global logging level to any of these options using the `SetGlobalLevel` function in the zerolog package, passing in one of the given constants above, e.g. `zerolog.InfoLevel` would be the "info" level. Whichever level is chosen, all logs with a level greater than or equal to that level will be written. To turn off logging entirely, pass the `zerolog.Disabled` constant.
#### Setting Global Log Level
This example uses command-line flags to demonstrate various outputs depending on the chosen log level.
```go
package main
import (
"flag"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
zerolog.TimeFieldFormat = ""
debug := flag.Bool("debug", false, "sets log level to debug")
flag.Parse()
// Default level for this example is info, unless debug flag is present
zerolog.SetGlobalLevel(zerolog.InfoLevel)
if *debug {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
}
log.Debug().Msg("This message appears only when log level set to Debug")
log.Info().Msg("This message appears when log level set to Debug or Info")
if e := log.Debug(); e.Enabled() {
// Compute log output only if enabled.
value := "bar"
e.Str("foo", value).Msg("some debug message")
}
}
```
Info Output (no flag)
```bash
$ ./logLevelExample
{"time":1516387492,"level":"info","message":"This message appears when log level set to Debug or Info"}
```
Debug Output (debug flag set)
```bash
$ ./logLevelExample -debug
{"time":1516387573,"level":"debug","message":"This message appears only when log level set to Debug"}
{"time":1516387573,"level":"info","message":"This message appears when log level set to Debug or Info"}
{"time":1516387573,"level":"debug","foo":"bar","message":"some debug message"}
```
#### Logging without Level or Message
You may choose to log without a specific level by using the `Log` method. You may also write without a message by setting an empty string in the `msg string` parameter of the `Msg` method. Both are demonstrated in the example below.
```go
package main
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
zerolog.TimeFieldFormat = ""
log.Log().
Str("foo", "bar").
Msg("")
}
// Output: {"time":1494567715,"foo":"bar"}
```
#### Logging Fatal Messages
```go
package main
import (
"errors"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
err := errors.New("A repo man spends his life getting into tense situations")
service := "myservice"
zerolog.TimeFieldFormat = ""
log.Fatal().
Err(err).
Str("service", service).
Msgf("Cannot start %s", service)
}
// Output: {"time":1516133263,"level":"fatal","error":"A repo man spends his life getting into tense situations","service":"myservice","message":"Cannot start myservice"}
// exit status 1
```
> NOTE: Using `Msgf` generates one allocation even when the logger is disabled.
### Create logger instance to manage different outputs
```go
logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
logger.Info().Str("foo", "bar").Msg("hello world")
// Output: {"level":"info","time":1494567715,"message":"hello world","foo":"bar"}
```
### Sub-loggers let you chain loggers with additional context
```go
sublogger := log.With().
Str("component", "foo").
Logger()
sublogger.Info().Msg("hello world")
// Output: {"level":"info","time":1494567715,"message":"hello world","component":"foo"}
```
### Pretty logging
To log a human-friendly, colorized output, use `zerolog.ConsoleWriter`:
```go
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
log.Info().Str("foo", "bar").Msg("Hello world")
// Output: 3:04PM INF Hello World foo=bar
```
To customize the configuration and formatting:
```go
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
output.FormatLevel = func(i interface{}) string {
return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
}
output.FormatMessage = func(i interface{}) string {
return fmt.Sprintf("***%s****", i)
}
output.FormatFieldName = func(i interface{}) string {
return fmt.Sprintf("%s:", i)
}
output.FormatFieldValue = func(i interface{}) string {
return strings.ToUpper(fmt.Sprintf("%s", i))
}
log := zerolog.New(output).With().Timestamp().Logger()
log.Info().Str("foo", "bar").Msg("Hello World")
// Output: 2006-01-02T15:04:05Z07:00 | INFO | ***Hello World**** foo:BAR
```
### Sub dictionary
```go
log.Info().
Str("foo", "bar").
Dict("dict", zerolog.Dict().
Str("bar", "baz").
Int("n", 1),
).Msg("hello world")
// Output: {"level":"info","time":1494567715,"foo":"bar","dict":{"bar":"baz","n":1},"message":"hello world"}
```
### Customize automatic field names
```go
zerolog.TimestampFieldName = "t"
zerolog.LevelFieldName = "l"
zerolog.MessageFieldName = "m"
log.Info().Msg("hello world")
// Output: {"l":"info","t":1494567715,"m":"hello world"}
```
### Add contextual fields to the global logger
```go
log.Logger = log.With().Str("foo", "bar").Logger()
```
### Add file and line number to log
```go
log.Logger = log.With().Caller().Logger()
log.Info().Msg("hello world")
// Output: {"level": "info", "message": "hello world", "caller": "/go/src/your_project/some_file:21"}
```
### Thread-safe, lock-free, non-blocking writer
If your writer might be slow or not thread-safe and you need your log producers to never get slowed down by a slow writer, you can use a `diode.Writer` as follow:
```go
wr := diode.NewWriter(os.Stdout, 1000, 10*time.Millisecond, func(missed int) {
fmt.Printf("Logger Dropped %d messages", missed)
})
log := zerolog.New(w)
log.Print("test")
```
You will need to install `code.cloudfoundry.org/go-diodes` to use this feature.
### Log Sampling
```go
sampled := log.Sample(&zerolog.BasicSampler{N: 10})
sampled.Info().Msg("will be logged every 10 messages")
// Output: {"time":1494567715,"level":"info","message":"will be logged every 10 messages"}
```
More advanced sampling:
```go
// Will let 5 debug messages per period of 1 second.
// Over 5 debug message, 1 every 100 debug messages are logged.
// Other levels are not sampled.
sampled := log.Sample(zerolog.LevelSampler{
DebugSampler: &zerolog.BurstSampler{
Burst: 5,
Period: 1*time.Second,
NextSampler: &zerolog.BasicSampler{N: 100},
},
})
sampled.Debug().Msg("hello world")
// Output: {"time":1494567715,"level":"debug","message":"hello world"}
```
### Hooks
```go
type SeverityHook struct{}
func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
if level != zerolog.NoLevel {
e.Str("severity", level.String())
}
}
hooked := log.Hook(SeverityHook{})
hooked.Warn().Msg("")
// Output: {"level":"warn","severity":"warn"}
```
### Pass a sub-logger by context
```go
ctx := log.With().Str("component", "module").Logger().WithContext(ctx)
log.Ctx(ctx).Info().Msg("hello world")
// Output: {"component":"module","level":"info","message":"hello world"}
```
### Set as standard logger output
```go
log := zerolog.New(os.Stdout).With().
Str("foo", "bar").
Logger()
stdlog.SetFlags(0)
stdlog.SetOutput(log)
stdlog.Print("hello world")
// Output: {"foo":"bar","message":"hello world"}
```
### Integration with `net/http`
The `github.com/rs/zerolog/hlog` package provides some helpers to integrate zerolog with `http.Handler`.
In this example we use [alice](https://github.com/justinas/alice) to install logger for better readability.
```go
log := zerolog.New(os.Stdout).With().
Timestamp().
Str("role", "my-service").
Str("host", host).
Logger()
c := alice.New()
// Install the logger handler with default output on the console
c = c.Append(hlog.NewHandler(log))
// Install some provided extra handler to set some request's context fields.
// Thanks to those handler, all our logs will come with some pre-populated fields.
c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
hlog.FromRequest(r).Info().
Str("method", r.Method).
Str("url", r.URL.String()).
Int("status", status).
Int("size", size).
Dur("duration", duration).
Msg("")
}))
c = c.Append(hlog.RemoteAddrHandler("ip"))
c = c.Append(hlog.UserAgentHandler("user_agent"))
c = c.Append(hlog.RefererHandler("referer"))
c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))
// Here is your final handler
h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Get the logger from the request's context. You can safely assume it
// will be always there: if the handler is removed, hlog.FromRequest
// will return a no-op logger.
hlog.FromRequest(r).Info().
Str("user", "current user").
Str("status", "ok").
Msg("Something happened")
// Output: {"level":"info","time":"2001-02-03T04:05:06Z","role":"my-service","host":"local-hostname","req_id":"b4g0l5t6tfid6dtrapu0","user":"current user","status":"ok","message":"Something happened"}
}))
http.Handle("/", h)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal().Err(err).Msg("Startup failed")
}
```
## Global Settings
Some settings can be changed and will by applied to all loggers:
* `log.Logger`: You can set this value to customize the global logger (the one used by package level methods).
* `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Set this to `zerolog.Disabled` to disable logging altogether (quiet mode).
* `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events.
* `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name.
* `zerolog.LevelFieldName`: Can be set to customize level field name.
* `zerolog.MessageFieldName`: Can be set to customize message field name.
* `zerolog.ErrorFieldName`: Can be set to customize `Err` field name.
* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with an empty string, times are formated as UNIX timestamp.
// DurationFieldUnit defines the unit for time.Duration type fields added
// using the Dur method.
* `DurationFieldUnit`: Sets the unit of the fields added by `Dur` (default: `time.Millisecond`).
* `DurationFieldInteger`: If set to true, `Dur` fields are formatted as integers instead of floats.
* `ErrorHandler`: Called whenever zerolog fails to write an event on its output. If not set, an error is printed on the stderr. This handler must be thread safe and non-blocking.
## Field Types
### Standard Types
* `Str`
* `Bool`
* `Int`, `Int8`, `Int16`, `Int32`, `Int64`
* `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64`
* `Float32`, `Float64`
### Advanced Fields
* `Err`: Takes an `error` and render it as a string using the `zerolog.ErrorFieldName` field name.
* `Timestamp`: Insert a timestamp field with `zerolog.TimestampFieldName` field name and formatted using `zerolog.TimeFieldFormat`.
* `Time`: Adds a field with the time formated with the `zerolog.TimeFieldFormat`.
* `Dur`: Adds a field with a `time.Duration`.
* `Dict`: Adds a sub-key/value as a field of the event.
* `Interface`: Uses reflection to marshal the type.
## Binary Encoding
In addition to the default JSON encoding, `zerolog` can produce binary logs using [CBOR](http://cbor.io) encoding. The choice of encoding can be decided at compile time using the build tag `binary_log` as follows:
```bash
go build -tags binary_log .
```
To Decode binary encoded log files you can use any CBOR decoder. One has been tested to work
with zerolog library is [CSD](https://github.com/toravir/csd/).
## Related Projects
* [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog`
## Benchmarks
See [logbench](http://hackemist.com/logbench/) for more comprehensive and up-to-date benchmarks.
All operations are allocation free (those numbers *include* JSON encoding):
```text
BenchmarkLogEmpty-8 100000000 19.1 ns/op 0 B/op 0 allocs/op
BenchmarkDisabled-8 500000000 4.07 ns/op 0 B/op 0 allocs/op
BenchmarkInfo-8 30000000 42.5 ns/op 0 B/op 0 allocs/op
BenchmarkContextFields-8 30000000 44.9 ns/op 0 B/op 0 allocs/op
BenchmarkLogFields-8 10000000 184 ns/op 0 B/op 0 allocs/op
```
There are a few Go logging benchmarks and comparisons that include zerolog.
* [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench)
* [uber-common/zap](https://github.com/uber-go/zap#performance)
Using Uber's zap comparison benchmark:
Log a message and 10 fields:
| Library | Time | Bytes Allocated | Objects Allocated |
| :--- | :---: | :---: | :---: |
| zerolog | 767 ns/op | 552 B/op | 6 allocs/op |
| :zap: zap | 848 ns/op | 704 B/op | 2 allocs/op |
| :zap: zap (sugared) | 1363 ns/op | 1610 B/op | 20 allocs/op |
| go-kit | 3614 ns/op | 2895 B/op | 66 allocs/op |
| lion | 5392 ns/op | 5807 B/op | 63 allocs/op |
| logrus | 5661 ns/op | 6092 B/op | 78 allocs/op |
| apex/log | 15332 ns/op | 3832 B/op | 65 allocs/op |
| log15 | 20657 ns/op | 5632 B/op | 93 allocs/op |
Log a message with a logger that already has 10 fields of context:
| Library | Time | Bytes Allocated | Objects Allocated |
| :--- | :---: | :---: | :---: |
| zerolog | 52 ns/op | 0 B/op | 0 allocs/op |
| :zap: zap | 283 ns/op | 0 B/op | 0 allocs/op |
| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
| lion | 2702 ns/op | 4074 B/op | 38 allocs/op |
| go-kit | 3378 ns/op | 3046 B/op | 52 allocs/op |
| logrus | 4309 ns/op | 4564 B/op | 63 allocs/op |
| apex/log | 13456 ns/op | 2898 B/op | 51 allocs/op |
| log15 | 14179 ns/op | 2642 B/op | 44 allocs/op |
Log a static string, without any context or `printf`-style templating:
| Library | Time | Bytes Allocated | Objects Allocated |
| :--- | :---: | :---: | :---: |
| zerolog | 50 ns/op | 0 B/op | 0 allocs/op |
| :zap: zap | 236 ns/op | 0 B/op | 0 allocs/op |
| standard library | 453 ns/op | 80 B/op | 2 allocs/op |
| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
| go-kit | 508 ns/op | 656 B/op | 13 allocs/op |
| lion | 771 ns/op | 1224 B/op | 10 allocs/op |
| logrus | 1244 ns/op | 1505 B/op | 27 allocs/op |
| apex/log | 2751 ns/op | 584 B/op | 11 allocs/op |
| log15 | 5181 ns/op | 1592 B/op | 26 allocs/op |
## Caveats
Note that zerolog does de-duplication fields. Using the same key multiple times creates multiple keys in final JSON:
```go
logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
logger.Info().
Timestamp().
Msg("dup")
// Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
```
However, it’s not a big deal as JSON accepts dup keys; the last one prevails.

+ 1
- 0
vendor/github.com/rs/zerolog/_config.yml

@ -0,0 +1 @@
remote_theme: rs/gh-readme

+ 224
- 0
vendor/github.com/rs/zerolog/array.go

@ -0,0 +1,224 @@
package zerolog
import (
"net"
"sync"
"time"
)
var arrayPool = &sync.Pool{
New: func() interface{} {
return &Array{
buf: make([]byte, 0, 500),
}
},
}
// Array is used to prepopulate an array of items
// which can be re-used to add to log messages.
type Array struct {
buf []byte
}
func putArray(a *Array) {
// Proper usage of a sync.Pool requires each entry to have approximately
// the same memory cost. To obtain this property when the stored type
// contains a variably-sized buffer, we add a hard limit on the maximum buffer
// to place back in the pool.
//
// See https://golang.org/issue/23199
const maxSize = 1 << 16 // 64KiB
if cap(a.buf) > maxSize {
return
}
arrayPool.Put(a)
}
// Arr creates an array to be added to an Event or Context.
func Arr() *Array {
a := arrayPool.Get().(*Array)
a.buf = a.buf[:0]
return a
}
// MarshalZerologArray method here is no-op - since data is
// already in the needed format.
func (*Array) MarshalZerologArray(*Array) {
}
func (a *Array) write(dst []byte) []byte {
dst = enc.AppendArrayStart(dst)
if len(a.buf) > 0 {
dst = append(append(dst, a.buf...))
}
dst = enc.AppendArrayEnd(dst)
putArray(a)
return dst
}
// Object marshals an object that implement the LogObjectMarshaler
// interface and append append it to the array.
func (a *Array) Object(obj LogObjectMarshaler) *Array {
e := Dict()
obj.MarshalZerologObject(e)
e.buf = enc.AppendEndMarker(e.buf)
a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
putEvent(e)
return a
}
// Str append append the val as a string to the array.
func (a *Array) Str(val string) *Array {
a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), val)
return a
}
// Bytes append append the val as a string to the array.
func (a *Array) Bytes(val []byte) *Array {
a.buf = enc.AppendBytes(enc.AppendArrayDelim(a.buf), val)
return a
}
// Hex append append the val as a hex string to the array.
func (a *Array) Hex(val []byte) *Array {
a.buf = enc.AppendHex(enc.AppendArrayDelim(a.buf), val)
return a
}
// Err serializes and appends the err to the array.
func (a *Array) Err(err error) *Array {
marshaled := ErrorMarshalFunc(err)
switch m := marshaled.(type) {
case LogObjectMarshaler:
e := newEvent(nil, 0)
e.buf = e.buf[:0]
e.appendObject(m)
a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
putEvent(e)
case error:
a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m.Error())
case string:
a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m)
default:
a.buf = enc.AppendInterface(enc.AppendArrayDelim(a.buf), m)
}
return a
}
// Bool append append the val as a bool to the array.
func (a *Array) Bool(b bool) *Array {
a.buf = enc.AppendBool(enc.AppendArrayDelim(a.buf), b)
return a
}
// Int append append i as a int to the array.
func (a *Array) Int(i int) *Array {
a.buf = enc.AppendInt(enc.AppendArrayDelim(a.buf), i)
return a
}
// Int8 append append i as a int8 to the array.
func (a *Array) Int8(i int8) *Array {
a.buf = enc.AppendInt8(enc.AppendArrayDelim(a.buf), i)
return a
}
// Int16 append append i as a int16 to the array.
func (a *Array) Int16(i int16) *Array {
a.buf = enc.AppendInt16(enc.AppendArrayDelim(a.buf), i)
return a
}
// Int32 append append i as a int32 to the array.
func (a *Array) Int32(i int32) *Array {
a.buf = enc.AppendInt32(enc.AppendArrayDelim(a.buf), i)
return a
}
// Int64 append append i as a int64 to the array.
func (a *Array) Int64(i int64) *Array {
a.buf = enc.AppendInt64(enc.AppendArrayDelim(a.buf), i)
return a
}
// Uint append append i as a uint to the array.
func (a *Array) Uint(i uint) *Array {
a.buf = enc.AppendUint(enc.AppendArrayDelim(a.buf), i)
return a
}
// Uint8 append append i as a uint8 to the array.
func (a *Array) Uint8(i uint8) *Array {
a.buf = enc.AppendUint8(enc.AppendArrayDelim(a.buf), i)
return a
}
// Uint16 append append i as a uint16 to the array.
func (a *Array) Uint16(i uint16) *Array {
a.buf = enc.AppendUint16(enc.AppendArrayDelim(a.buf), i)
return a
}
// Uint32 append append i as a uint32 to the array.
func (a *Array) Uint32(i uint32) *Array {
a.buf = enc.AppendUint32(enc.AppendArrayDelim(a.buf), i)
return a
}
// Uint64 append append i as a uint64 to the array.
func (a *Array) Uint64(i uint64) *Array {
a.buf = enc.AppendUint64(enc.AppendArrayDelim(a.buf), i)
return a
}
// Float32 append append f as a float32 to the array.
func (a *Array) Float32(f float32) *Array {
a.buf = enc.AppendFloat32(enc.AppendArrayDelim(a.buf), f)
return a
}
// Float64 append append f as a float64 to the array.
func (a *Array) Float64(f float64) *Array {
a.buf = enc.AppendFloat64(enc.AppendArrayDelim(a.buf), f)
return a
}
// Time append append t formated as string using zerolog.TimeFieldFormat.