Compare commits

...

19 Commits

Author SHA1 Message Date
8ec35c49fa doc/README: add links for archived releases 2020-05-06 17:54:04 +12:00
3b9e077e75 vendor: remove dep metadata files 2018-12-31 18:02:28 +13:00
7fd260cefb doc: move README to top-level md file 2018-11-07 19:00:25 +13:00
b0195be43d build: extra ldflags 2018-11-07 18:57:11 +13:00
6b4334cfc8 convert to go modules 2018-11-07 18:55:32 +13:00
327709cb10 doc: remove TODO.txt (moved to Gitea issues) 2018-11-07 18:52:26 +13:00
047c3deb89 hg2git: convert ignores, remove hgtags 2018-11-07 18:51:54 +13:00
e14a5a2839 bump next version to 1.0.2 2018-06-10 14:21:52 +12:00
00cd788cce Added tag v1.0.1 for changeset a865c1797be4 2018-06-10 14:21:38 +12:00
dec185942f doc: changelog for 1.0.1 2018-06-10 14:21:06 +12:00
4d40499f8e doc: add go-get tags 2018-06-10 14:20:59 +12:00
e95c49e487 send dc message when contacts are shared in TG 2018-06-10 14:16:12 +12:00
edabc2b597 add missing captions on voiceclip uploads 2018-06-10 11:14:08 +12:00
43a9b41dc3 add missing captions on photo uploads 2018-06-10 11:13:19 +12:00
47d740c98a patch missing format string 2018-06-09 19:11:01 +12:00
dd6d1c73bb doc: format README for code.ivysaur.me 2018-06-09 18:45:04 +12:00
3dc931aa36 doc: clean up TODO 2018-06-09 18:39:04 +12:00
bf785cc057 bump version to 1.0.1 2018-06-09 18:36:28 +12:00
3345905f91 Added tag v1.0.0 for changeset 1e6ec1004b02 2018-06-09 18:36:03 +12:00
24 changed files with 111 additions and 2059 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
config.json
nmdc-telegramfrontend
dist/*-src*
dist/*-linux64*

View File

@ -1,6 +0,0 @@
mode:regex
^config\.json$
^nmdc-telegramfrontend$
^dist/.+-src*
^dist/.+-linux64*

39
Gopkg.lock generated
View File

@ -1,39 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "code.ivysaur.me/libnmdc"
packages = ["."]
revision = "74c20aaed6b2034c9dfb14e95b25aeca0b35d6fe"
[[projects]]
branch = "master"
name = "github.com/cxmcc/tiger"
packages = ["."]
revision = "bde35e2713d7f674987c2ecb21a6b0fc33749516"
[[projects]]
name = "github.com/go-telegram-bot-api/telegram-bot-api"
packages = ["."]
revision = "0e0af0c480ea98e982d5f4d45fb39577c6ab1e3e"
version = "v4.6.2"
[[projects]]
name = "github.com/technoweenie/multipartstreamer"
packages = ["."]
revision = "a90a01d73ae432e2611d178c18367fbaa13e0154"
version = "v1.0.1"
[[projects]]
name = "gopkg.in/h2non/filetype.v1"
packages = [".","matchers","types"]
revision = "cc14fdc9ca0e4c2bafad7458f6ff79fd3947cfbb"
version = "v1.0.5"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "7607d6cc9aef0b2e3b855ecf48dbbfc6efa20a4e8efee3e9cbd5e8d1e1f0c68a"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,34 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# 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"
[[constraint]]
name = "github.com/go-telegram-bot-api/telegram-bot-api"
version = "4.6.2"
[[constraint]]
name = "code.ivysaur.me/libnmdc"
branch = "master"
[[constraint]]
name = "gopkg.in/h2non/filetype.v1"
version = "1.0.5"

View File

@ -1,6 +1,6 @@
# Makefile for nmdc-telegramfrontend
VERSION := 1.0.0
VERSION := 1.0.2
.PHONY: all dist clean
@ -10,7 +10,7 @@ clean:
rm -f ./nmdc-telegramfrontend
nmdc-telegramfrontend: *.go Gopkg.*
GOOS=linux GOARCH=amd64 go build
GOOS=linux GOARCH=amd64 go build -ldflags '-s -w'
dist: dist/nmdc-telegramfrontend-$(VERSION)-linux64.tar.xz dist/nmdc-telegramfrontend-$(VERSION)-src.tar.bz2

View File

@ -285,7 +285,7 @@ func (this *NTFServer) HandleHubMessage(msg upstreamMessage) {
}
case libnmdc.EVENT_CONNECTION_STATE_CHANGED:
log.Printf("Hub(%s): * Connection %s", msg.evt.StateChange.String())
log.Printf("Hub(%s): * Connection %s", msg.hubNick, msg.evt.StateChange.String())
case libnmdc.EVENT_PRIVATE:
err := this.DirectMessageTelegramUser(msg.telegramUserId, fmt.Sprintf("PM from user '%s': %s", msg.evt.Nick, msg.evt.Message))
@ -467,7 +467,7 @@ func (this *NTFServer) HandleGroupMessage(update telegram.Update) error {
if update.Message.Photo != nil {
go func() {
conUrl, err := this.ContentedUploadBestSync(*update.Message.Photo)
this.uploadAsyncComplete(userID, "photo", conUrl, err, "")
this.uploadAsyncComplete(userID, "photo", conUrl, err, update.Message.Caption)
}()
}
@ -499,12 +499,19 @@ func (this *NTFServer) HandleGroupMessage(update telegram.Update) error {
if update.Message.Voice != nil {
go func() {
conUrl, err := this.ContentedUploadSync(update.Message.Voice.FileID, int64(update.Message.Voice.FileSize)) // no thumbnail fallback available
this.uploadAsyncComplete(userID, "voiceclip", conUrl, err, fmt.Sprintf("Voice clip (duration %ds): ", update.Message.Voice.Duration))
this.uploadAsyncComplete(userID, "voiceclip", conUrl, err, fmt.Sprintf("Voice clip (duration %ds): %s", update.Message.Voice.Duration, update.Message.Caption))
}()
}
}
if update.Message.Contact != nil {
return this.HubSay(userID, fmt.Sprintf("Contact %s %s %s %s",
update.Message.Contact.FirstName, update.Message.Contact.LastName,
update.Message.Contact.PhoneNumber,
update.Message.Caption))
}
if update.Message.Location != nil {
return this.HubSay(userID, fmt.Sprintf(
"Latitude %f Longitude %f %s",

64
README.md Normal file
View File

@ -0,0 +1,64 @@
# nmdc-telegramfrontend
![](https://img.shields.io/badge/written%20in-Go-blue.svg)
A bot to synchronise chat between an DC hub and a Telegram supergroup.
## Features
- Supports NMDC / NMDCS / ADC / ADCS hubs
- Automatically upload photos/files/videos/audio/stickers to a Contented server for DC users
- Fallback upload to thumbnail-only if exceeding declared Contented file size limit
- Convert telegram quoted messages to DC quoting style
- Exclude messages from multiple hub nicks (e.g. `Hub-Security` / `PtokaX` and helper bots)
- Standalone binary
- PM with native users
- Get native userlist inside the telegram group chat
- Option for length requirement on native nick
- Disconnect telegram users who fail to log in to the upstream hub
## Setup
Create a new telegram bot
1. Use BotFather to create a new bot
2. Use BotFather to disable its privacy mode for group chats
3. Use BotFather to add commands (that appear in the groupchat). Recommendation: `userlist - List native online users`
Create a telegram group
1. Manually create a group chat and add the bot to it
2. Convert group chat to supergroup
3. Grant bot to be an administrator (including ability to add more administrators)
4. Settings > "Who can add members" > Only administrators
5. Create an invite link
Handover to `nmdc-telegramfrontend`
1. Run this bot with no `-GroupChatID`, to learn the groupchat ID
2. Post a test message in the group chat, to discover the groupchat ID
3. Leave the group chat (long press on mobile, can't do it on desktop)
4. Run this bot with `-GroupChatID` for normal operation
## Usage
Chat with the bot to enter/leave the synchronised channel.
Sometimes the telegram invite links can take a few minutes to activate, especially if there has been unusual activity (e.g. frequent join/parts)
## Changelog
2018-06-10 1.0.1
- Send DC captions on TG photos and voice clips
- Send DC message when sharing a TG contact
- Fix a cosmetic issue with missing format specifier in log message
- [⬇️ nmdc-telegramfrontend-1.0.1-src.tar.bz2](https://git.ivysaur.me/attachments/857a8a57-3dae-44b4-9250-8c3691b393e1) *(100.84 KiB)*
- [⬇️ nmdc-telegramfrontend-1.0.1-linux64.tar.xz](https://git.ivysaur.me/attachments/86a10b0f-0ca5-4150-820c-e27451198a27) *(1.92 MiB)*
2018-06-09 1.0.0
- Initial public release
- [⬇️ nmdc-telegramfrontend-1.0.0-src.tar.bz2](https://git.ivysaur.me/attachments/0d357ce3-b026-45d0-b722-8f4f0cd47be5) *(101.17 KiB)*
- [⬇️ nmdc-telegramfrontend-1.0.0-linux64.tar.xz](https://git.ivysaur.me/attachments/643ad20b-814a-4883-94fa-876a4c361a83) *(1.92 MiB)*
2018-06-03
- Private beta

View File

@ -1,29 +0,0 @@
## NEXT
[ ] Attachment support
[ ] Telegram -> NMDC
[ ] Convert telegram files/images/videos to raw URLs for NMDC
[ ] Bot API does allow downloading - but authenticated - need to re-host (either internally or upload to contented server)
[ ] Can download up to 20MB files, raise contented limit to match
[ ] Some photos and stickers come in multiple filesizes, from original down to thumbnail - take the largest size that fits in the Contented limit(?)
[ ] Display warning in groupchat if message cannot be delivered to native users, or if it was delivered at low fidelity
[ ] Including private messages??? is that possible?
[ ] Publish
## FUTURE
- Nick translation
- Editing Messages support
- show up in the hub as {message} **
- Drop hub connections for inactive telegram users
- no way to measure inactivity??
- Support passworded hub nicks
- Better/automatic detection of when an upstream disconnection should be reflected in telegram
- Being kicked hub-side should also lose the groupchat connection

51
dist/README.txt vendored
View File

@ -1,51 +0,0 @@
A bot to synchronise chat between an DC hub and a Telegram supergroup.
Tags: NMDC
Written in Go
## FEATURES
- Supports NMDC / NMDCS / ADC / ADCS hubs
- Automatically upload photos/files/videos/audio/stickers to a Contented server for DC users
- Fallback upload to thumbnail-only if exceeding declared Contented file size limit
- Convert telegram quoted messages to DC quoting style
- Exclude messages from multiple hub nicks (e.g. `Hub-Security` / `PtokaX` and helper bots)
- Standalone binary
- PM with native users
- Get native userlist inside the telegram group chat
- Option for length requirement on native nick
- Disconnect telegram users who fail to log in to the upstream hub
## SETUP
Create a new telegram bot
- Use BotFather to create a new bot
- Use BotFather to disable its privacy mode for group chats
- Use BotFather to add commands (that appear in the groupchat). Recommendation: `userlist - List native online users`
Create a telegram group
- Manually create a group chat and add the bot to it
- Convert group chat to supergroup
- Grant bot to be an administrator (including ability to add more administrators)
- Settings > "Who can add members" > Only administrators
- Create an invite link
Handover to nmdc-telegramfrontend
- Run this bot with no -GroupChatID, to learn the groupchat ID
- Post a test message in the group chat, to discover the groupchat ID
- Leave the group chat (long press on mobile, can't do it on desktop)
- Run this bot with -GroupChatID for normal operation
## USAGE
Chat with the bot to enter/leave the synchronised channel.
Sometimes the telegram invite links can take a few minutes to activate, especially if there has been unusual activity (e.g. frequent join/parts)
## CHANGELOG
2018-06-09 v1.0.0
- Initial public release
2018-06-03
- Private beta

9
go.mod Normal file
View File

@ -0,0 +1,9 @@
module code.ivysaur.me/nmdc-telegramfrontend
require (
code.ivysaur.me/libnmdc v0.0.0-20180604072808-74c20aaed6b2
github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7
github.com/go-telegram-bot-api/telegram-bot-api v4.6.2+incompatible
github.com/technoweenie/multipartstreamer v1.0.1
gopkg.in/h2non/filetype.v1 v1.0.5
)

10
go.sum Normal file
View File

@ -0,0 +1,10 @@
code.ivysaur.me/libnmdc v0.0.0-20180604072808-74c20aaed6b2 h1:D00qrIfNnmDsirAREmrCsjhr1nr/VnmM7Btx3rvZKb4=
code.ivysaur.me/libnmdc v0.0.0-20180604072808-74c20aaed6b2/go.mod h1:ZHCjIX/zm29hd2H8YtzOBHiSyowZNOHvgoMRjKbBLLg=
github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7 h1:jBEtq1t2gpn2kEzvRlCUxvvrxl5aSWkXNPwe/hwvSNQ=
github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7/go.mod h1:ruCYvt9rtYymAr4rNmfYJrl1dz8HSXUFP7cufqKOsDI=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.2+incompatible h1:tI1+S63aiYb8JDRY8WBqn7Q1Utnr09L9ga5T2VA2ZDI=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.2+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
gopkg.in/h2non/filetype.v1 v1.0.5 h1:CC1jjJjoEhNVbMhXYalmGBhOBK2V70Q1N850wt/98/Y=
gopkg.in/h2non/filetype.v1 v1.0.5/go.mod h1:M0yem4rwSX5lLVrkEuRRp2/NinFMD5vgJ4DlAhZcfNo=

View File

@ -1,58 +0,0 @@
package libnmdc
import (
"fmt"
)
func ExampleHubConnectionOptions_Connect() {
opts := HubConnectionOptions{
Address: "127.0.0.1",
Self: NewUserInfo("slowpoke9"),
}
events := make(chan HubEvent, 0)
hub := ConnectAsync(&opts, events)
for event := range events {
switch event.EventType {
case EVENT_CONNECTION_STATE_CHANGED:
fmt.Printf("Connection -- %s (%s)\n", event.StateChange, event.Message)
case EVENT_PUBLIC:
fmt.Printf("Message from '%s': '%s'\n", event.Nick, event.Message)
if event.Message == "how are you" {
hub.SayPublic("good thanks!")
}
default:
fmt.Printf("%+v\n", event)
}
}
}
func ExampleHubConnectionOptions_ConnectSync() {
cb := func(hub *HubConnection, event HubEvent) {
switch event.EventType {
case EVENT_CONNECTION_STATE_CHANGED:
fmt.Printf("Connection -- %s (%s)\n", event.StateChange, event.Message)
case EVENT_PUBLIC:
fmt.Printf("Message from '%s': '%s'\n", event.Nick, event.Message)
if event.Message == "how are you" {
hub.SayPublic("good thanks!")
}
default:
fmt.Printf("%+v\n", event)
}
}
opts := HubConnectionOptions{
Address: "127.0.0.1",
Self: NewUserInfo("slowpoke9"),
}
ConnectSync(&opts, cb) // blocking
}

View File

@ -1,97 +0,0 @@
package libnmdc
import (
"testing"
)
func TestMyINFOParse(t *testing.T) {
np := NewNmdcProtocol(nil).(*NmdcProtocol)
type myInfoTestPair struct {
in string
expect UserInfo
}
cases := []myInfoTestPair{
myInfoTestPair{
in: "$ALL Bxxxy description<ApexDC++ V:1.4.3,M:P,H:9/0/2,S:1>$ $0.01\x01$xyz@example.com$53054999578$",
expect: UserInfo{
Nick: "Bxxxy",
Description: "description",
ClientTag: "ApexDC++",
ClientVersion: "1.4.3",
Email: "xyz@example.com",
ShareSize: 53054999578,
Flag: FLAG_NORMAL,
Slots: 1,
HubsUnregistered: 9,
HubsRegistered: 0,
HubsOperator: 2,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
Speed: "0.0",
},
},
},
myInfoTestPair{
in: "$ALL ixxxxxxx0 $P$10A$$0$",
expect: UserInfo{
Nick: "ixxxxxxx0",
ClientVersion: "0", // Auto-inserted by the parser for short-format MyINFO strings
Flag: UserFlag(rune('A')),
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
Speed: "1",
},
},
},
myInfoTestPair{
in: "$ALL SXXXX_XXXXXXR <ncdc V:1.19.1-12-g5561,M:P,H:1/0/0,S:10>$ $0.005Q$$0$",
expect: UserInfo{
Nick: "SXXXX_XXXXXXR",
ClientTag: "ncdc",
ClientVersion: "1.19.1-12-g5561",
Flag: UserFlag(rune('Q')),
Slots: 10,
HubsUnregistered: 1,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
Speed: "0.00",
},
},
},
myInfoTestPair{
in: "$ALL mxxxu desccccc<HexChat V:2.12.1,M:P,H:1/0/0,S:0>$ $p$$0$",
expect: UserInfo{
Nick: "mxxxu",
Description: "desccccc",
ClientTag: "HexChat",
ClientVersion: "2.12.1",
Flag: UserFlag(rune('p')),
HubsUnregistered: 1,
Slots: 0,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
},
},
},
}
for _, v := range cases {
got, err := np.parseMyINFO(v.in)
if err != nil {
t.Errorf("MyINFO parse warning (%s)", err.Error())
continue
}
if *got != v.expect {
t.Errorf("MyINFO parse failure\nExpected:\n%+v\nGot:\n%+v\n", v.expect, got)
continue
}
}
}

View File

@ -1,36 +0,0 @@
package libnmdc
import (
"strings"
"testing"
)
func TestTTH(t *testing.T) {
// echo -n 'hello world' | tthsum
testCases := [][2]string{
[2]string{"hello world", "ZIIVRZDR2FD3W4KKNMNYUU3765LPPK7BWY64CHI"},
[2]string{"", "LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ"},
[2]string{"\x00", "VK54ZIEEVTWNAUI5D5RDFIL37LX2IQNSTAXFKSA"},
[2]string{strings.Repeat("A", 1024), "L66Q4YVNAFWVS23X2HJIRA5ZJ7WXR3F26RSASFA"},
}
short := func(s string) string {
if len(s) > 15 {
return s[0:15] + "..."
}
return s
}
for _, testCase := range testCases {
input, expected := testCase[0], testCase[1]
result, err := TTH(input)
if err != nil {
t.Fatalf("Error getting TTH for '%s': %s", short(input), err.Error())
}
if Base32(result) != expected {
t.Fatalf("Wrong TTH for '%s' (got '%s' expected '%s')", short(input), result, expected)
}
}
}

View File

@ -1,144 +0,0 @@
package tiger
import (
"fmt"
"io"
"strings"
"testing"
"unsafe"
)
type Test struct {
out string
in string
}
var golden = []Test{
{"3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3", ""},
{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f5f247809", "a"},
{"2aab1484e8c158f2bfb8c5ff41b57a525129131c957b5f93", "abc"},
{"d981f8cb78201a950dcf3048751e441c517fca1aa55a29f6", "message digest"},
{"1714a472eee57d30040412bfcc55032a0b11602ff37beee9", "abcdefghijklmnopqrstuvwxyz"},
{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e7b53f78e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
{"8dcea680a17583ee502ba38a3c368651890ffbccdc49a8cc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
{"1c14795529fd9f207a958f84c52f11e887fa0cabdfd91bfd", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
{"cdf0990c5c6b6b0bddd63a75ed20e2d448bf44e15fde0df4", strings.Repeat("A", 1024)},
{"89292aee0f82842abc080c57b3aadd9ca84d66bf0cae77aa", strings.Repeat("A", 1025)},
}
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
c := New()
buf := make([]byte, len(g.in)+4)
for j := 0; j < 7; j++ {
if j < 2 {
io.WriteString(c, g.in)
} else if j == 2 {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
} else if j > 2 {
// test unaligned write
buf = buf[1:]
copy(buf, g.in)
c.Write(buf[:len(g.in)])
}
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("tiger[%d](%s) = %s want %s", j, g.in, s, g.out)
}
c.Reset()
}
}
}
type WriteTest struct {
out int
in string
}
var writeTestVectors = []WriteTest{
{0, ""},
{1, "A"},
{2, "AA"},
{10, strings.Repeat("A", 10)},
{1024, strings.Repeat("A", 1024)},
{1025, strings.Repeat("A", 1025)},
{0, ""},
}
func TestWriteReturnsCorrectSize(t *testing.T) {
c := New()
for i := 0; i < len(writeTestVectors); i++ {
v := writeTestVectors[i]
b := []byte(v.in)
length, err := c.Write(b[:len(v.in)])
if length != v.out {
t.Fatalf("Write() = %d want %d", length, v.out)
}
if err != nil {
t.Fatalf("Write(%s) failed.", v.in)
}
}
}
func ExampleNew() {
h := New()
io.WriteString(h, "It's the eye of the tiger, it's the thrill of the fight")
io.WriteString(h, "Rising up to the challenge of our rival!")
fmt.Printf("%x", h.Sum(nil))
// Output: a7bbad36cc17918e399ae8ee893e4595e4d24e1639fe822c
}
func ExampleNew2() {
h := New2()
io.WriteString(h, "It's the eye of the tiger, it's the thrill of the fight")
io.WriteString(h, "Rising up to the challenge of our rival!")
fmt.Printf("%x", h.Sum(nil))
// Output: c86695c2a639506682de2c12c2d23b61a12db78ea1ee1001
}
var bench = New()
var buf = make([]byte, 8192+1)
var sum = make([]byte, bench.Size())
func benchmarkSize(b *testing.B, size int, unaligned bool) {
b.SetBytes(int64(size))
buf := buf
if unaligned {
if uintptr(unsafe.Pointer(&buf[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
buf = buf[1:]
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
bench.Reset()
bench.Write(buf[:size])
bench.Sum(sum[:0])
}
}
func BenchmarkHash8Bytes(b *testing.B) {
benchmarkSize(b, 8, false)
}
func BenchmarkHash1K(b *testing.B) {
benchmarkSize(b, 1024, false)
}
func BenchmarkHash8K(b *testing.B) {
benchmarkSize(b, 8192, false)
}
func BenchmarkHash8BytesUnaligned(b *testing.B) {
benchmarkSize(b, 8, true)
}
func BenchmarkHash1KUnaligned(b *testing.B) {
benchmarkSize(b, 1024, true)
}
func BenchmarkHash8KUnaligned(b *testing.B) {
benchmarkSize(b, 8192, true)
}

View File

@ -1,682 +0,0 @@
package tgbotapi_test
import (
"io/ioutil"
"log"
"net/http"
"os"
"testing"
"time"
"github.com/go-telegram-bot-api/telegram-bot-api"
)
const (
TestToken = "153667468:AAHlSHlMqSt1f_uFmVRJbm5gntu2HI4WW8I"
ChatID = 76918703
SupergroupChatID = -1001120141283
ReplyToMessageID = 35
ExistingPhotoFileID = "AgADAgADw6cxG4zHKAkr42N7RwEN3IFShCoABHQwXEtVks4EH2wBAAEC"
ExistingDocumentFileID = "BQADAgADOQADjMcoCcioX1GrDvp3Ag"
ExistingAudioFileID = "BQADAgADRgADjMcoCdXg3lSIN49lAg"
ExistingVoiceFileID = "AwADAgADWQADjMcoCeul6r_q52IyAg"
ExistingVideoFileID = "BAADAgADZgADjMcoCav432kYe0FRAg"
ExistingVideoNoteFileID = "DQADAgADdQAD70cQSUK41dLsRMqfAg"
ExistingStickerFileID = "BQADAgADcwADjMcoCbdl-6eB--YPAg"
)
func getBot(t *testing.T) (*tgbotapi.BotAPI, error) {
bot, err := tgbotapi.NewBotAPI(TestToken)
bot.Debug = true
if err != nil {
t.Error(err)
t.Fail()
}
return bot, err
}
func TestNewBotAPI_notoken(t *testing.T) {
_, err := tgbotapi.NewBotAPI("")
if err == nil {
t.Error(err)
t.Fail()
}
}
func TestGetUpdates(t *testing.T) {
bot, _ := getBot(t)
u := tgbotapi.NewUpdate(0)
_, err := bot.GetUpdates(u)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithMessage(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api")
msg.ParseMode = "markdown"
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithMessageReply(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api")
msg.ReplyToMessageID = ReplyToMessageID
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithMessageForward(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewForward(ChatID, ChatID, ReplyToMessageID)
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewPhoto(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewPhotoUpload(ChatID, "tests/image.jpg")
msg.Caption = "Test"
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewPhotoWithFileBytes(t *testing.T) {
bot, _ := getBot(t)
data, _ := ioutil.ReadFile("tests/image.jpg")
b := tgbotapi.FileBytes{Name: "image.jpg", Bytes: data}
msg := tgbotapi.NewPhotoUpload(ChatID, b)
msg.Caption = "Test"
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewPhotoWithFileReader(t *testing.T) {
bot, _ := getBot(t)
f, _ := os.Open("tests/image.jpg")
reader := tgbotapi.FileReader{Name: "image.jpg", Reader: f, Size: -1}
msg := tgbotapi.NewPhotoUpload(ChatID, reader)
msg.Caption = "Test"
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewPhotoReply(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewPhotoUpload(ChatID, "tests/image.jpg")
msg.ReplyToMessageID = ReplyToMessageID
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithExistingPhoto(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewPhotoShare(ChatID, ExistingPhotoFileID)
msg.Caption = "Test"
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewDocument(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewDocumentUpload(ChatID, "tests/image.jpg")
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithExistingDocument(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewDocumentShare(ChatID, ExistingDocumentFileID)
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewAudio(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewAudioUpload(ChatID, "tests/audio.mp3")
msg.Title = "TEST"
msg.Duration = 10
msg.Performer = "TEST"
msg.MimeType = "audio/mpeg"
msg.FileSize = 688
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithExistingAudio(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewAudioShare(ChatID, ExistingAudioFileID)
msg.Title = "TEST"
msg.Duration = 10
msg.Performer = "TEST"
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewVoice(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewVoiceUpload(ChatID, "tests/voice.ogg")
msg.Duration = 10
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithExistingVoice(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewVoiceShare(ChatID, ExistingVoiceFileID)
msg.Duration = 10
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithContact(t *testing.T) {
bot, _ := getBot(t)
contact := tgbotapi.NewContact(ChatID, "5551234567", "Test")
if _, err := bot.Send(contact); err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithLocation(t *testing.T) {
bot, _ := getBot(t)
_, err := bot.Send(tgbotapi.NewLocation(ChatID, 40, 40))
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithVenue(t *testing.T) {
bot, _ := getBot(t)
venue := tgbotapi.NewVenue(ChatID, "A Test Location", "123 Test Street", 40, 40)
if _, err := bot.Send(venue); err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewVideo(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewVideoUpload(ChatID, "tests/video.mp4")
msg.Duration = 10
msg.Caption = "TEST"
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithExistingVideo(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewVideoShare(ChatID, ExistingVideoFileID)
msg.Duration = 10
msg.Caption = "TEST"
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewVideoNote(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewVideoNoteUpload(ChatID, 240, "tests/videonote.mp4")
msg.Duration = 10
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithExistingVideoNote(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewVideoNoteShare(ChatID, 240, ExistingVideoNoteFileID)
msg.Duration = 10
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewSticker(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewStickerUpload(ChatID, "tests/image.jpg")
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithExistingSticker(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewStickerShare(ChatID, ExistingStickerFileID)
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithNewStickerAndKeyboardHide(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewStickerUpload(ChatID, "tests/image.jpg")
msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{
RemoveKeyboard: true,
Selective: false,
}
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewStickerShare(ChatID, ExistingStickerFileID)
msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{
RemoveKeyboard: true,
Selective: false,
}
_, err := bot.Send(msg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestGetFile(t *testing.T) {
bot, _ := getBot(t)
file := tgbotapi.FileConfig{FileID: ExistingPhotoFileID}
_, err := bot.GetFile(file)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendChatConfig(t *testing.T) {
bot, _ := getBot(t)
_, err := bot.Send(tgbotapi.NewChatAction(ChatID, tgbotapi.ChatTyping))
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSendEditMessage(t *testing.T) {
bot, _ := getBot(t)
msg, err := bot.Send(tgbotapi.NewMessage(ChatID, "Testing editing."))
if err != nil {
t.Error(err)
t.Fail()
}
edit := tgbotapi.EditMessageTextConfig{
BaseEdit: tgbotapi.BaseEdit{
ChatID: ChatID,
MessageID: msg.MessageID,
},
Text: "Updated text.",
}
_, err = bot.Send(edit)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestGetUserProfilePhotos(t *testing.T) {
bot, _ := getBot(t)
_, err := bot.GetUserProfilePhotos(tgbotapi.NewUserProfilePhotos(ChatID))
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestSetWebhookWithCert(t *testing.T) {
bot, _ := getBot(t)
time.Sleep(time.Second * 2)
bot.RemoveWebhook()
wh := tgbotapi.NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem")
_, err := bot.SetWebhook(wh)
if err != nil {
t.Error(err)
t.Fail()
}
info, err := bot.GetWebhookInfo()
if err != nil {
t.Error(err)
}
if info.LastErrorDate != 0 {
t.Errorf("[Telegram callback failed]%s", info.LastErrorMessage)
}
bot.RemoveWebhook()
}
func TestSetWebhookWithoutCert(t *testing.T) {
bot, _ := getBot(t)
time.Sleep(time.Second * 2)
bot.RemoveWebhook()
wh := tgbotapi.NewWebhook("https://example.com/tgbotapi-test/" + bot.Token)
_, err := bot.SetWebhook(wh)
if err != nil {
t.Error(err)
t.Fail()
}
info, err := bot.GetWebhookInfo()
if err != nil {
t.Error(err)
}
if info.LastErrorDate != 0 {
t.Errorf("[Telegram callback failed]%s", info.LastErrorMessage)
}
bot.RemoveWebhook()
}
func TestUpdatesChan(t *testing.T) {
bot, _ := getBot(t)
var ucfg tgbotapi.UpdateConfig = tgbotapi.NewUpdate(0)
ucfg.Timeout = 60
_, err := bot.GetUpdatesChan(ucfg)
if err != nil {
t.Error(err)
t.Fail()
}
}
func ExampleNewBotAPI() {
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
if err != nil {
log.Panic(err)
}
bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName)
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates, err := bot.GetUpdatesChan(u)
// Optional: wait for updates and clear them if you don't want to handle
// a large backlog of old messages
time.Sleep(time.Millisecond * 500)
updates.Clear()
for update := range updates {
if update.Message == nil {
continue
}
log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)
msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text)
msg.ReplyToMessageID = update.Message.MessageID
bot.Send(msg)
}
}
func ExampleNewWebhook() {
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
if err != nil {
log.Fatal(err)
}
bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName)
_, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem"))
if err != nil {
log.Fatal(err)
}
info, err := bot.GetWebhookInfo()
if err != nil {
log.Fatal(err)
}
if info.LastErrorDate != 0 {
log.Printf("[Telegram callback failed]%s", info.LastErrorMessage)
}
updates := bot.ListenForWebhook("/" + bot.Token)
go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil)
for update := range updates {
log.Printf("%+v\n", update)
}
}
func ExampleAnswerInlineQuery() {
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") // create new bot
if err != nil {
log.Panic(err)
}
log.Printf("Authorized on account %s", bot.Self.UserName)
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates, err := bot.GetUpdatesChan(u)
for update := range updates {
if update.InlineQuery == nil { // if no inline query, ignore it
continue
}
article := tgbotapi.NewInlineQueryResultArticle(update.InlineQuery.ID, "Echo", update.InlineQuery.Query)
article.Description = update.InlineQuery.Query
inlineConf := tgbotapi.InlineConfig{
InlineQueryID: update.InlineQuery.ID,
IsPersonal: true,
CacheTime: 0,
Results: []interface{}{article},
}
if _, err := bot.AnswerInlineQuery(inlineConf); err != nil {
log.Println(err)
}
}
}
func TestDeleteMessage(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api")
msg.ParseMode = "markdown"
message, _ := bot.Send(msg)
deleteMessageConfig := tgbotapi.DeleteMessageConfig{
ChatID: message.Chat.ID,
MessageID: message.MessageID,
}
_, err := bot.DeleteMessage(deleteMessageConfig)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestPinChatMessage(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api")
msg.ParseMode = "markdown"
message, _ := bot.Send(msg)
pinChatMessageConfig := tgbotapi.PinChatMessageConfig{
ChatID: message.Chat.ID,
MessageID: message.MessageID,
DisableNotification: false,
}
_, err := bot.PinChatMessage(pinChatMessageConfig)
if err != nil {
t.Error(err)
t.Fail()
}
}
func TestUnpinChatMessage(t *testing.T) {
bot, _ := getBot(t)
msg := tgbotapi.NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api")
msg.ParseMode = "markdown"
message, _ := bot.Send(msg)
// We need pin message to unpin something
pinChatMessageConfig := tgbotapi.PinChatMessageConfig{
ChatID: message.Chat.ID,
MessageID: message.MessageID,
DisableNotification: false,
}
_, err := bot.PinChatMessage(pinChatMessageConfig)
unpinChatMessageConfig := tgbotapi.UnpinChatMessageConfig{
ChatID: message.Chat.ID,
}
_, err = bot.UnpinChatMessage(unpinChatMessageConfig)
if err != nil {
t.Error(err)
t.Fail()
}
}

View File

@ -1,177 +0,0 @@
package tgbotapi_test
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"testing"
)
func TestNewInlineQueryResultArticle(t *testing.T) {
result := tgbotapi.NewInlineQueryResultArticle("id", "title", "message")
if result.Type != "article" ||
result.ID != "id" ||
result.Title != "title" ||
result.InputMessageContent.(tgbotapi.InputTextMessageContent).Text != "message" {
t.Fail()
}
}
func TestNewInlineQueryResultArticleMarkdown(t *testing.T) {
result := tgbotapi.NewInlineQueryResultArticleMarkdown("id", "title", "*message*")
if result.Type != "article" ||
result.ID != "id" ||
result.Title != "title" ||
result.InputMessageContent.(tgbotapi.InputTextMessageContent).Text != "*message*" ||
result.InputMessageContent.(tgbotapi.InputTextMessageContent).ParseMode != "Markdown" {
t.Fail()
}
}
func TestNewInlineQueryResultArticleHTML(t *testing.T) {
result := tgbotapi.NewInlineQueryResultArticleHTML("id", "title", "<b>message</b>")
if result.Type != "article" ||
result.ID != "id" ||
result.Title != "title" ||
result.InputMessageContent.(tgbotapi.InputTextMessageContent).Text != "<b>message</b>" ||
result.InputMessageContent.(tgbotapi.InputTextMessageContent).ParseMode != "HTML" {
t.Fail()
}
}
func TestNewInlineQueryResultGIF(t *testing.T) {
result := tgbotapi.NewInlineQueryResultGIF("id", "google.com")
if result.Type != "gif" ||
result.ID != "id" ||
result.URL != "google.com" {
t.Fail()
}
}
func TestNewInlineQueryResultMPEG4GIF(t *testing.T) {
result := tgbotapi.NewInlineQueryResultMPEG4GIF("id", "google.com")
if result.Type != "mpeg4_gif" ||
result.ID != "id" ||
result.URL != "google.com" {
t.Fail()
}
}
func TestNewInlineQueryResultPhoto(t *testing.T) {
result := tgbotapi.NewInlineQueryResultPhoto("id", "google.com")
if result.Type != "photo" ||
result.ID != "id" ||
result.URL != "google.com" {
t.Fail()
}
}
func TestNewInlineQueryResultPhotoWithThumb(t *testing.T) {
result := tgbotapi.NewInlineQueryResultPhotoWithThumb("id", "google.com", "thumb.com")
if result.Type != "photo" ||
result.ID != "id" ||
result.URL != "google.com" ||
result.ThumbURL != "thumb.com" {
t.Fail()
}
}
func TestNewInlineQueryResultVideo(t *testing.T) {
result := tgbotapi.NewInlineQueryResultVideo("id", "google.com")
if result.Type != "video" ||
result.ID != "id" ||
result.URL != "google.com" {
t.Fail()
}
}
func TestNewInlineQueryResultAudio(t *testing.T) {
result := tgbotapi.NewInlineQueryResultAudio("id", "google.com", "title")
if result.Type != "audio" ||
result.ID != "id" ||
result.URL != "google.com" ||
result.Title != "title" {
t.Fail()
}
}
func TestNewInlineQueryResultVoice(t *testing.T) {
result := tgbotapi.NewInlineQueryResultVoice("id", "google.com", "title")
if result.Type != "voice" ||
result.ID != "id" ||
result.URL != "google.com" ||
result.Title != "title" {
t.Fail()
}
}
func TestNewInlineQueryResultDocument(t *testing.T) {
result := tgbotapi.NewInlineQueryResultDocument("id", "google.com", "title", "mime/type")
if result.Type != "document" ||
result.ID != "id" ||
result.URL != "google.com" ||
result.Title != "title" ||
result.MimeType != "mime/type" {
t.Fail()
}
}
func TestNewInlineQueryResultLocation(t *testing.T) {
result := tgbotapi.NewInlineQueryResultLocation("id", "name", 40, 50)
if result.Type != "location" ||
result.ID != "id" ||
result.Title != "name" ||
result.Latitude != 40 ||
result.Longitude != 50 {
t.Fail()
}
}
func TestNewEditMessageText(t *testing.T) {
edit := tgbotapi.NewEditMessageText(ChatID, ReplyToMessageID, "new text")
if edit.Text != "new text" ||
edit.BaseEdit.ChatID != ChatID ||
edit.BaseEdit.MessageID != ReplyToMessageID {
t.Fail()
}
}
func TestNewEditMessageCaption(t *testing.T) {
edit := tgbotapi.NewEditMessageCaption(ChatID, ReplyToMessageID, "new caption")
if edit.Caption != "new caption" ||
edit.BaseEdit.ChatID != ChatID ||
edit.BaseEdit.MessageID != ReplyToMessageID {
t.Fail()
}
}
func TestNewEditMessageReplyMarkup(t *testing.T) {
markup := tgbotapi.InlineKeyboardMarkup{
InlineKeyboard: [][]tgbotapi.InlineKeyboardButton{
[]tgbotapi.InlineKeyboardButton{
tgbotapi.InlineKeyboardButton{Text: "test"},
},
},
}
edit := tgbotapi.NewEditMessageReplyMarkup(ChatID, ReplyToMessageID, markup)
if edit.ReplyMarkup.InlineKeyboard[0][0].Text != "test" ||
edit.BaseEdit.ChatID != ChatID ||
edit.BaseEdit.MessageID != ReplyToMessageID {
t.Fail()
}
}

View File

@ -1,200 +0,0 @@
package tgbotapi_test
import (
"testing"
"time"
"github.com/go-telegram-bot-api/telegram-bot-api"
)
func TestUserStringWith(t *testing.T) {
user := tgbotapi.User{
ID: 0,
FirstName: "Test",
LastName: "Test",
UserName: "",
LanguageCode: "en",
IsBot: false,
}
if user.String() != "Test Test" {
t.Fail()
}
}
func TestUserStringWithUserName(t *testing.T) {
user := tgbotapi.User{
ID: 0,
FirstName: "Test",
LastName: "Test",
UserName: "@test",
LanguageCode: "en",
}
if user.String() != "@test" {
t.Fail()
}
}
func TestMessageTime(t *testing.T) {
message := tgbotapi.Message{Date: 0}
date := time.Unix(0, 0)
if message.Time() != date {
t.Fail()
}
}
func TestMessageIsCommandWithCommand(t *testing.T) {
message := tgbotapi.Message{Text: "/command"}
message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
if message.IsCommand() != true {
t.Fail()
}
}
func TestIsCommandWithText(t *testing.T) {
message := tgbotapi.Message{Text: "some text"}
if message.IsCommand() != false {
t.Fail()
}
}
func TestIsCommandWithEmptyText(t *testing.T) {
message := tgbotapi.Message{Text: ""}
if message.IsCommand() != false {
t.Fail()
}
}
func TestCommandWithCommand(t *testing.T) {
message := tgbotapi.Message{Text: "/command"}
message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
if message.Command() != "command" {
t.Fail()
}
}
func TestCommandWithEmptyText(t *testing.T) {
message := tgbotapi.Message{Text: ""}
if message.Command() != "" {
t.Fail()
}
}
func TestCommandWithNonCommand(t *testing.T) {
message := tgbotapi.Message{Text: "test text"}
if message.Command() != "" {
t.Fail()
}
}
func TestCommandWithBotName(t *testing.T) {
message := tgbotapi.Message{Text: "/command@testbot"}
message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}}
if message.Command() != "command" {
t.Fail()
}
}
func TestCommandWithAtWithBotName(t *testing.T) {
message := tgbotapi.Message{Text: "/command@testbot"}
message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}}
if message.CommandWithAt() != "command@testbot" {
t.Fail()
}
}
func TestMessageCommandArgumentsWithArguments(t *testing.T) {
message := tgbotapi.Message{Text: "/command with arguments"}
message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
if message.CommandArguments() != "with arguments" {
t.Fail()
}
}
func TestMessageCommandArgumentsWithMalformedArguments(t *testing.T) {
message := tgbotapi.Message{Text: "/command-without argument space"}
message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
if message.CommandArguments() != "without argument space" {
t.Fail()
}
}
func TestMessageCommandArgumentsWithoutArguments(t *testing.T) {
message := tgbotapi.Message{Text: "/command"}
if message.CommandArguments() != "" {
t.Fail()
}
}
func TestMessageCommandArgumentsForNonCommand(t *testing.T) {
message := tgbotapi.Message{Text: "test text"}
if message.CommandArguments() != "" {
t.Fail()
}
}
func TestMessageEntityParseURLGood(t *testing.T) {
entity := tgbotapi.MessageEntity{URL: "https://www.google.com"}
if _, err := entity.ParseURL(); err != nil {
t.Fail()
}
}
func TestMessageEntityParseURLBad(t *testing.T) {
entity := tgbotapi.MessageEntity{URL: ""}
if _, err := entity.ParseURL(); err == nil {
t.Fail()
}
}
func TestChatIsPrivate(t *testing.T) {
chat := tgbotapi.Chat{ID: 10, Type: "private"}
if chat.IsPrivate() != true {
t.Fail()
}
}
func TestChatIsGroup(t *testing.T) {
chat := tgbotapi.Chat{ID: 10, Type: "group"}
if chat.IsGroup() != true {
t.Fail()
}
}
func TestChatIsChannel(t *testing.T) {
chat := tgbotapi.Chat{ID: 10, Type: "channel"}
if chat.IsChannel() != true {
t.Fail()
}
}
func TestChatIsSuperGroup(t *testing.T) {
chat := tgbotapi.Chat{ID: 10, Type: "supergroup"}
if !chat.IsSuperGroup() {
t.Fail()
}
}
func TestFileLink(t *testing.T) {
file := tgbotapi.File{FilePath: "test/test.txt"}
if file.Link("token") != "https://api.telegram.org/file/bottoken/test/test.txt" {
t.Fail()
}
}

View File

@ -1,124 +0,0 @@
package multipartstreamer
import (
"bytes"
"io"
"io/ioutil"
"mime/multipart"
"os"
"path/filepath"
"testing"
)
func TestMultipartFile(t *testing.T) {
path, _ := os.Getwd()
file := filepath.Join(path, "multipartstreamer.go")
stat, _ := os.Stat(file)
ms := New()
err := ms.WriteFields(map[string]string{"a": "b"})
if err != nil {
t.Fatalf("Error writing fields: %s", err)
}
err = ms.WriteFile("file", file)
if err != nil {
t.Fatalf("Error writing file: %s", err)
}
diff := ms.Len() - stat.Size()
if diff != 363 {
t.Error("Unexpected multipart size")
}
data, err := ioutil.ReadAll(ms.GetReader())
if err != nil {
t.Fatalf("Error reading multipart data: %s", err)
}
buf := bytes.NewBuffer(data)
reader := multipart.NewReader(buf, ms.Boundary())
part, err := reader.NextPart()
if err != nil {
t.Fatalf("Expected form field: %s", err)
}
if str := part.FileName(); str != "" {
t.Errorf("Unexpected filename: %s", str)
}
if str := part.FormName(); str != "a" {
t.Errorf("Unexpected form name: %s", str)
}
if by, _ := ioutil.ReadAll(part); string(by) != "b" {
t.Errorf("Unexpected form value: %s", string(by))
}
part, err = reader.NextPart()
if err != nil {
t.Fatalf("Expected file field: %s", err)
}
if str := part.FileName(); str != "multipartstreamer.go" {
t.Errorf("Unexpected filename: %s", str)
}
if str := part.FormName(); str != "file" {
t.Errorf("Unexpected form name: %s", str)
}
src, _ := ioutil.ReadFile(file)
if by, _ := ioutil.ReadAll(part); string(by) != string(src) {
t.Errorf("Unexpected file value")
}
part, err = reader.NextPart()
if err != io.EOF {
t.Errorf("Unexpected 3rd part: %s", part)
}
}
func TestMultipartReader(t *testing.T) {
ms := New()
err := ms.WriteReader("file", "code/bass", 3, bytes.NewBufferString("ABC"))
if err != nil {
t.Fatalf("Error writing reader: %s", err)
}
if size := ms.Len(); size != 244 {
t.Errorf("Unexpected multipart size: %d", size)
}
data, err := ioutil.ReadAll(ms.GetReader())
if err != nil {
t.Fatalf("Error reading multipart data: %s", err)
}
buf := bytes.NewBuffer(data)
reader := multipart.NewReader(buf, ms.Boundary())
part, err := reader.NextPart()
if err != nil {
t.Fatalf("Expected file field: %s", err)
}
if str := part.FileName(); str != "code/bass" {
t.Errorf("Unexpected filename: %s", str)
}
if str := part.FormName(); str != "file" {
t.Errorf("Unexpected form name: %s", str)
}
if by, _ := ioutil.ReadAll(part); string(by) != "ABC" {
t.Errorf("Unexpected file value")
}
part, err = reader.NextPart()
if err != io.EOF {
t.Errorf("Unexpected 2nd part: %s", part)
}
}

View File

@ -1,123 +0,0 @@
package filetype
import (
"testing"
"gopkg.in/h2non/filetype.v1/types"
)
func TestIs(t *testing.T) {
cases := []struct {
buf []byte
ext string
match bool
}{
{[]byte{0xFF, 0xD8, 0xFF}, "jpg", true},
{[]byte{0xFF, 0xD8, 0x00}, "jpg", false},
{[]byte{0x89, 0x50, 0x4E, 0x47}, "png", true},
}
for _, test := range cases {
if Is(test.buf, test.ext) != test.match {
t.Fatalf("Invalid match: %s", test.ext)
}
}
}
func TestIsType(t *testing.T) {
cases := []struct {
buf []byte
kind types.Type
match bool
}{
{[]byte{0xFF, 0xD8, 0xFF}, types.Get("jpg"), true},
{[]byte{0xFF, 0xD8, 0x00}, types.Get("jpg"), false},
{[]byte{0x89, 0x50, 0x4E, 0x47}, types.Get("png"), true},
}
for _, test := range cases {
if IsType(test.buf, test.kind) != test.match {
t.Fatalf("Invalid match: %s", test.kind.Extension)
}
}
}
func TestIsMIME(t *testing.T) {
cases := []struct {
buf []byte
mime string
match bool
}{
{[]byte{0xFF, 0xD8, 0xFF}, "image/jpeg", true},
{[]byte{0xFF, 0xD8, 0x00}, "image/jpeg", false},
{[]byte{0x89, 0x50, 0x4E, 0x47}, "image/png", true},
}
for _, test := range cases {
if IsMIME(test.buf, test.mime) != test.match {
t.Fatalf("Invalid match: %s", test.mime)
}
}
}
func TestIsSupported(t *testing.T) {
cases := []struct {
ext string
match bool
}{
{"jpg", true},
{"jpeg", false},
{"abc", false},
{"png", true},
{"mp4", true},
{"", false},
}
for _, test := range cases {
if IsSupported(test.ext) != test.match {
t.Fatalf("Invalid match: %s", test.ext)
}
}
}
func TestIsMIMESupported(t *testing.T) {
cases := []struct {
mime string
match bool
}{
{"image/jpeg", true},
{"foo/bar", false},
{"image/png", true},
{"video/mpeg", true},
}
for _, test := range cases {
if IsMIMESupported(test.mime) != test.match {
t.Fatalf("Invalid match: %s", test.mime)
}
}
}
func TestAddType(t *testing.T) {
AddType("foo", "foo/foo")
if !IsSupported("foo") {
t.Fatalf("Not supported extension")
}
if !IsMIMESupported("foo/foo") {
t.Fatalf("Not supported MIME type")
}
}
func TestGetType(t *testing.T) {
jpg := GetType("jpg")
if jpg == types.Unknown {
t.Fatalf("Type should be supported")
}
invalid := GetType("invalid")
if invalid != Unknown {
t.Fatalf("Type should not be supported")
}
}

View File

@ -1,40 +0,0 @@
package filetype
import (
"testing"
)
func TestKind(t *testing.T) {
var cases = []struct {
buf []byte
ext string
}{
{[]byte{0xFF, 0xD8, 0xFF}, "jpg"},
{[]byte{0x89, 0x50, 0x4E, 0x47}, "png"},
{[]byte{0x89, 0x0, 0x0}, "unknown"},
}
for _, test := range cases {
kind, _ := Image(test.buf)
if kind.Extension != test.ext {
t.Fatalf("Invalid match: %s != %s", kind.Extension, test.ext)
}
}
}
func TestIsKind(t *testing.T) {
var cases = []struct {
buf []byte
match bool
}{
{[]byte{0xFF, 0xD8, 0xFF}, true},
{[]byte{0x89, 0x50, 0x4E, 0x47}, true},
{[]byte{0x89, 0x0, 0x0}, false},
}
for _, test := range cases {
if IsImage(test.buf) != test.match {
t.Fatalf("Invalid match: %t", test.match)
}
}
}

View File

@ -1,187 +0,0 @@
package filetype
import (
"bytes"
"io"
"io/ioutil"
"testing"
"gopkg.in/h2non/filetype.v1/matchers"
"gopkg.in/h2non/filetype.v1/types"
)
func TestMatch(t *testing.T) {
cases := []struct {
buf []byte
ext string
}{
{[]byte{0xFF, 0xD8, 0xFF}, "jpg"},
{[]byte{0xFF, 0xD8, 0x00}, "unknown"},
{[]byte{0x89, 0x50, 0x4E, 0x47}, "png"},
}
for _, test := range cases {
match, err := Match(test.buf)
if err != nil {
t.Fatalf("Error: %s", err)
}
if match.Extension != test.ext {
t.Fatalf("Invalid image type: %s != %s", match.Extension, test.ext)
}
}
}
func TestMatchFile(t *testing.T) {
cases := []struct {
ext string
}{
{"gif"},
{"jpg"},
{"png"},
{"zip"},
{"tar"},
{"tif"},
{"mp4"},
}
for _, test := range cases {
kind, _ := MatchFile("./fixtures/sample." + test.ext)
if kind.Extension != test.ext {
t.Fatalf("Invalid image type: %s != %s", kind.Extension, test.ext)
}
}
}
func TestMatchReader(t *testing.T) {
cases := []struct {
buf io.Reader
ext string
}{
{bytes.NewBuffer([]byte{0xFF, 0xD8, 0xFF}), "jpg"},
{bytes.NewBuffer([]byte{0xFF, 0xD8, 0x00}), "unknown"},
{bytes.NewBuffer([]byte{0x89, 0x50, 0x4E, 0x47}), "png"},
}
for _, test := range cases {
match, err := MatchReader(test.buf)
if err != nil {
t.Fatalf("Error: %s", err)
}
if match.Extension != test.ext {
t.Fatalf("Invalid image type: %s", match.Extension)
}
}
}
func TestMatches(t *testing.T) {
cases := []struct {
buf []byte
match bool
}{
{[]byte{0xFF, 0xD8, 0xFF}, true},
{[]byte{0xFF, 0x0, 0x0}, false},
{[]byte{0x89, 0x50, 0x4E, 0x47}, true},
}
for _, test := range cases {
if Matches(test.buf) != test.match {
t.Fatalf("Do not matches: %#v", test.buf)
}
}
}
func TestAddMatcher(t *testing.T) {
fileType := AddType("foo", "foo/foo")
AddMatcher(fileType, func(buf []byte) bool {
return len(buf) == 2 && buf[0] == 0x00 && buf[1] == 0x00
})
if !Is([]byte{0x00, 0x00}, "foo") {
t.Fatalf("Type cannot match")
}
if !IsSupported("foo") {
t.Fatalf("Not supported extension")
}
if !IsMIMESupported("foo/foo") {
t.Fatalf("Not supported MIME type")
}
}
func TestMatchMap(t *testing.T) {
cases := []struct {
buf []byte
kind types.Type
}{
{[]byte{0xFF, 0xD8, 0xFF}, types.Get("jpg")},
{[]byte{0x89, 0x50, 0x4E, 0x47}, types.Get("png")},
{[]byte{0xFF, 0x0, 0x0}, Unknown},
}
for _, test := range cases {
if kind := MatchMap(test.buf, matchers.Image); kind != test.kind {
t.Fatalf("Do not matches: %#v", test.buf)
}
}
}
func TestMatchesMap(t *testing.T) {
cases := []struct {
buf []byte
match bool
}{
{[]byte{0xFF, 0xD8, 0xFF}, true},
{[]byte{0x89, 0x50, 0x4E, 0x47}, true},
{[]byte{0xFF, 0x0, 0x0}, false},
}
for _, test := range cases {
if match := MatchesMap(test.buf, matchers.Image); match != test.match {
t.Fatalf("Do not matches: %#v", test.buf)
}
}
}
//
// Benchmarks
//
var tarBuffer, _ = ioutil.ReadFile("./fixtures/sample.tar")
var zipBuffer, _ = ioutil.ReadFile("./fixtures/sample.zip")
var jpgBuffer, _ = ioutil.ReadFile("./fixtures/sample.jpg")
var gifBuffer, _ = ioutil.ReadFile("./fixtures/sample.gif")
var pngBuffer, _ = ioutil.ReadFile("./fixtures/sample.png")
func BenchmarkMatchTar(b *testing.B) {
for n := 0; n < b.N; n++ {
Match(tarBuffer)
}
}
func BenchmarkMatchZip(b *testing.B) {
for n := 0; n < b.N; n++ {
Match(zipBuffer)
}
}
func BenchmarkMatchJpeg(b *testing.B) {
for n := 0; n < b.N; n++ {
Match(jpgBuffer)
}
}
func BenchmarkMatchGif(b *testing.B) {
for n := 0; n < b.N; n++ {
Match(gifBuffer)
}
}
func BenchmarkMatchPng(b *testing.B) {
for n := 0; n < b.N; n++ {
Match(pngBuffer)
}
}

View File

@ -1,27 +0,0 @@
package types
import "testing"
func TestSplit(t *testing.T) {
cases := []struct {
mime string
kind string
subtype string
}{
{"image/jpeg", "image", "jpeg"},
{"/jpeg", "", "jpeg"},
{"image/", "image", ""},
{"/", "", ""},
{"image", "image", ""},
}
for _, test := range cases {
kind, subtype := splitMime(test.mime)
if test.kind != kind {
t.Fatalf("Invalid kind: %s", test.kind)
}
if test.subtype != subtype {
t.Fatalf("Invalid subtype: %s", test.subtype)
}
}
}

12
vendor/modules.txt vendored Normal file
View File

@ -0,0 +1,12 @@
# code.ivysaur.me/libnmdc v0.0.0-20180604072808-74c20aaed6b2
code.ivysaur.me/libnmdc
# github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7
github.com/cxmcc/tiger
# github.com/go-telegram-bot-api/telegram-bot-api v4.6.2+incompatible
github.com/go-telegram-bot-api/telegram-bot-api
# github.com/technoweenie/multipartstreamer v1.0.1
github.com/technoweenie/multipartstreamer
# gopkg.in/h2non/filetype.v1 v1.0.5
gopkg.in/h2non/filetype.v1
gopkg.in/h2non/filetype.v1/matchers
gopkg.in/h2non/filetype.v1/types