• Stars
    star
    151
  • Rank 246,057 (Top 5 %)
  • Language
    Go
  • License
    Apache License 2.0
  • Created about 7 years ago
  • Updated almost 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Telegram MTProto and its proxy (over gRPC) in Go (golang). API Layer: 71

MTProto

Telegram MTProto and proxy (over gRPC) in Go (golang). Telegram API layer: 71

Quick start

# It is vendored in 'dep'. Refer to https://github.com/golang/dep for 'dep' installation.
dep ensure

# Run simple shell with your Telegram API id, hash, and, server address with your phone number.
# If you don't have Telegram API stuffs, get them from 'https://my.telegram.org/apps'.
go run examples/simpleshell/main.go <APIID> <APIHASH> <PHONE> <IP> <PORT>

# Then you can see 'Enter code:' message
# Telegram sends you an authentication code. Check it on your mobile or desktop app and put it.
Enter code: <YOUR_CODE>

# Now signed-in. Let's get your recent dialogs. 
# You can see them in JSON.
$ dialogs
....

# Quit the shell.
$ exit

# You can find 'credentials.json' file which keeps your MTProto secerets.
ls -al credentials.json

# You can check if the scerets correct by sign-in with it.
go run examples/simpleshell/main.go  credentials.json

Usage

You can find the real code at simpleshell.

Sign-in with key

// Mew MTProto manager
config, _ := mtproto.NewConfiguration(appVersion, deviceModel, systemVersion, language, 0, 0, "credentials.json")
manager, _ := mtproto.NewManager(config)

// Sign-in by key
mconn, _ := manager.LoadAuthentication()

Sign-in without key

// New MTProto manager
config, _ := mtproto.NewConfiguration(appVersion, deviceModel, systemVersion, language, 0, 0, "new-credentials.json")
manager, _ := mtproto.NewManager(config)

// Request to send an authentication code
// It needs your phone number and Telegram API stuffs you can check in https://my.telegram.org/apps 
mconn, sentCode, err := manager.NewAuthentication(phoneNumber, apiID, apiHash, ip, port)

// Get the code from user input
fmt.Scanf("%s", &code)

// Sign-in and generate the new key
_, err = mconn.SignIn(phoneNumber, code, sentCode.GetValue().PhoneCodeHash)

Telegram RPC in Protocol Buffer

cjongseok/mtproto implements TL-schema functions in Protocol Buffer. These are declared in types.tl.proto as RPCs, and implemented in Go at procs.tl.go. With the same interface, you can call functions not only in direct connection to the Telegram server, but also over a mtproto proxy.

Let's have two direct call examples, messages.getDialogs and messages.sendMessage.

Get dialogs

// New RPC caller with a connection to Telegram server. 
// By alternating mconn with a proxy connection, you can call same functions over proxy. It is covered later.
caller := mtproto.RPCaller{mconn}

// New input peer
// In Telegram DSL, Predicates inherit a Type.
// Here we create a Predicate, InputPeerEmpty whose parent is InputPeer.
// More details about these types are covered later.
emptyPeer := &mtproto.TypeInputPeer{&mtproto.TypeInputPeer_InputPeerEmpty{&mtproto.PredInputPeerEmpty{}}

// Query to Telegram
dialogs, _ := caller.MessagesGetDialogs(context.Background(), &mtproto.ReqMessagesGetDialogs{
    OffsetDate: 0,
    OffsetId: 	0,
    OffsetPeer: emptyPeer,
    Limit: 		1,
})

Send a message to a channel

// New RPC caller with a connection to Telegram server. 
caller := mtproto.RPCaller{mconn}

// New input peer
// Create a Predicate, InputPeerChannel, wraped by its parent Type, InputPeer.
channelPeer := &mtproto.TypeInputPeer{&mtproto.TypeInputPeer_InputPeerChannel{
    &mtproto.PredInputPeerChannel{
        yourChannelId, yourChannelHash,
    }}}

// Send a request to Telegram
caller.MessagesSendMessage(context.Background(), &mtproto.ReqMessagesSendMessage{
    Peer:      channelPeer,
    Message:   "Hello MTProto",
    RandomId:  rand.Int63(),
})

How mtproto is impelemented in Protocol Buffer

Types

Telegram's mtproto has three kinds of types, Type, Predicate, and Method. A Type is a kind of a data structure interface which has no fields, and a Predicate implements a Type. In the above case, mtproto.PredInputPeerChannel is a Predicate of a Type mtproto.TypeInputPeer. gRPC recommends to implement this kind of polymorphism with Oneof, so InputPeer is defined in Protocol Buffer as below:

// types.tl.proto:19
message TypeInputPeer {
	oneof Value {
		PredInputPeerEmpty InputPeerEmpty = 1;
		PredInputPeerSelf InputPeerSelf = 2;
		PredInputPeerChat InputPeerChat = 3;
		PredInputPeerUser InputPeerUser = 4;
		PredInputPeerChannel InputPeerChannel = 5;
    }
}

The use of gRPC Oneof in Go is complex, because Go does not allow hierarchical relations among types, e.g., inheritance. I believe, however, gRPC guys did their best and it would be the best implementation of such polymorphism in Go with RPC. For more details about the use of OneOf in Go, please refer to this document.

Methods

Mtproto methods have a namespace as you can see in TL-schema, e.g., auth, account, users, ... . Instead of managing these namespaces as separate Protocol Buffer services, these are integrated into one Protocol Buffer Service, Mtproto, and namesapces are remained as method name prefixes. In the above example, the original name of MessagesSendMessage is messages.sendMessage.

In the original schema, a method can have multiple parameters. These paremeters are declared into a new data structure in Protocol Buffer whose name starts with 'Req'. For example, messages.sendMessage requires many parameters, and these are transformed into ReqMessagesSendMessage in MessagesSendMessage.

Proxy

You can use the proxy in two purposes:

  • MTProto session sharing: Many proxy clients can use the same MTProto session on the proxy server.
  • MTProto in other languages: The proxy enables various languages on its client side, since it uses gRPC.

Server

As a stand-alone daemon

mtprotod is a stand-alone proxy daemon containing Telegram MTProto implementation in Go.

Quick Start

# start mtprotod at port 11011
docker run \
-p 11011: 11011 \
-v /your/mtproto/secrets/directory:/opt \
cjongseok/mtprotod start  \
--port 11011 \
--addr <Your_Telegram_server_address> \
--apiid <Your_Telegram_api_id> \
--apihash <Your_Telegram_api_hash> \
--phone <Your_Telegram_phone_number> \
--secrets /opt/<Your_MTProto_secrets_file_name>

# At mtproto/proxy, let's get dialogs through over the proxy
dep ensure
go test proxy/proxy_test.go --run TestDialogs

Build & Run

# In mtproto directory
dep ensure
go run mtprotod/main.go start \
--addr <Your_Telegram_server_address> \
--apiid <Your_Telegram_api_id> \
--apihash <Your_Telegram_api_hash> \
--phone <Your_Telegram_phone_number> \
--port <Proxy_port> \
--secrets /opt/<Your_MTProto_secrets_file_name>

As a part of Go application

Use mtproto/proxy package.

// New proxy server
config, _ := mtproto.NewConfiguration(apiId, apiHash, appVersion, deviceModel, systemVersion, language, 0, 0, key)
server = proxy.NewServer(port)

// Start the server
server.Start(config, phone)

Client in Go

// New proxy client
client, _ := proxy.NewClient(proxyAddr)

// Telegram RPC over proxy. It is same with the previous 'Get dialogs' but the RPC caller
emptyPeer := &mtproto.TypeInputPeer{&mtproto.TypeInputPeer_InputPeerEmpty{&mtproto.PredInputPeerEmpty{}}
dialogs, err := client.MessagesGetDialogs(context.Background(), &mtproto.ReqMessagesGetDialogs{
    OffsetDate: 0,
    OffsetId:   0,
    OffsetPeer: emptyPeer,
    Limit:      1,
})

Client in Python

See py.

Client in other languages

By compiling types.tl.proto and proxy/tl_update.proto, you can create clients in your preferred language.
For this, you need to install Protocol Buffer together with gRPC library of the target language. Then you can compile Protocol Buffer files with this kind of command lines:

You can find these command lines for other languages in gRPC tutorial.

Acknowledgement

License

Apache 2.0