Skip to content

MTGo Userbot Example ​

Userbots authenticate with a phone number instead of a bot token. This example covers the full authentication flow including SMS verification, 2FA password check, and session string export for reuse.

Full Code ​

go
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    tg "github.com/mtgo-labs/mtgo/telegram"
    "github.com/mtgo-labs/mtgo/telegram/types"
)

func main() {
    apiID := mustAtoi(mustEnv("API_ID"))
    apiHash := mustEnv("API_HASH")
    phone := mustEnv("PHONE")

    client, err := tg.NewClient(apiID, apiHash, &tg.Config{
        SessionName: "userbot",
        SavePeers:   true,
    })
    if err != nil {
        log.Fatalf("new client: %v", err)
    }

    if err := client.Connect(0); err != nil {
        log.Fatalf("connect: %v", err)
    }
    defer client.Stop()

    // Check if already authenticated
    if client.IsAuthorized() {
        log.Println("Already authenticated, skipping login...")
    } else {
        authenticate(client, phone)
    }

    // Export session string for reuse
    sessionStr, err := client.ExportSessionString()
    if err != nil {
        log.Printf("export session string: %v", err)
    } else {
        fmt.Println("Session string (save this for reuse):")
        fmt.Println(sessionStr)
    }

    // Get user info
    me, err := client.GetMe(context.Background())
    if err != nil {
        log.Fatalf("get me: %v", err)
    }
    fmt.Printf("Logged in as %s @%s\n", me.FirstName, me.Username)

    // Handle incoming messages
    client.OnMessage(func(c *tg.Client, msg *types.Message) {
        if msg.Out {
            return // skip own messages
        }
        if msg.Text == "" {
            return
        }

        log.Printf("[%d] %s: %s", msg.ChatID(), msg.SenderID(), msg.Text)
    }, tg.All)

    client.Idle()
}

func authenticate(client *tg.Client, phone string) {
    ctx := context.Background()

    // Step 1: Send verification code
    sentCode, err := client.SendCode(ctx, phone)
    if err != nil {
        log.Fatalf("send code: %v", err)
    }

    // Step 2: Prompt user for the code
    fmt.Printf("Enter the code sent to %s: ", phone)
    var code string
    fmt.Scanln(&code)

    // Step 3: Sign in with the code
    signInResult, err := client.SignIn(ctx, phone, code, sentCode.PhoneCodeHash)
    if err != nil {
        // Check if 2FA is required
        if tg.IsPasswordRequired(err) {
            password := promptPassword()
            _, err = client.CheckPassword(ctx, password)
            if err != nil {
                log.Fatalf("check password: %v", err)
            }
        } else {
            log.Fatalf("sign in: %v", err)
        }
    }

    if signInResult != nil {
        fmt.Printf("Signed in successfully!\n")
    } else {
        fmt.Printf("Signed in (via password)!\n")
    }
}

func promptPassword() string {
    fmt.Print("Enter your 2FA password: ")
    var password string
    fmt.Scanln(&password)
    return password
}

func mustEnv(key string) string {
    v := os.Getenv(key)
    if v == "" {
        log.Fatalf("environment variable %s is required", key)
    }
    return v
}

func mustAtoi(s string) int {
    var n int
    if _, err := fmt.Sscanf(s, "%d", &n); err != nil {
        log.Fatalf("invalid integer %q: %v", s, err)
    }
    return n
}

How It Works ​

Authentication Flow ​

Userbots follow a multi-step authentication process:

SendCode → user enters SMS code → SignIn → done
                                      ↓ (if 2FA enabled)
                                   CheckPassword → done

Step 1: Send Verification Code ​

go
sentCode, err := client.SendCode(ctx, phone)

This triggers Telegram to send a verification code to the phone number via SMS or Telegram. The sentCode object contains the PhoneCodeHash needed for the next step.

Step 2: Sign In with Code ​

go
signInResult, err := client.SignIn(ctx, phone, code, sentCode.PhoneCodeHash)

Submit the code the user received. If the account has 2FA enabled, this returns an error indicating a password is required.

Step 3: Handle 2FA (If Enabled) ​

go
if tg.IsPasswordRequired(err) {
    _, err = client.CheckPassword(ctx, password)
}

IsPasswordRequired checks if the error indicates 2FA. If so, CheckPassword submits the user's 2FA password.

Checking Authorization ​

go
if client.IsAuthorized() {
    // Session is already authenticated
}

On subsequent runs, if the session is still valid, the client skips authentication entirely. This is why session persistence matters.

Session String Export ​

go
sessionStr, err := client.ExportSessionString()

Export a portable session string that can be used to resume the session elsewhere:

go
// On a different machine:
client, err := tg.NewClient(apiID, apiHash, &tg.Config{
    SessionString: sessionStr,
})

WARNING

Session strings contain the full auth key. Store them securely and never share them.

Skipping Own Messages ​

go
if msg.Out {
    return // skip messages sent by the userbot
}

Unlike bots, userbots receive their own messages in updates. Filter them out with the msg.Out flag to avoid loops.

Message Handling ​

go
client.OnMessage(func(c *tg.Client, msg *types.Message) {
    log.Printf("[%d] %s: %s", msg.ChatID(), msg.SenderID(), msg.Text)
}, tg.All)

Use tg.All to receive messages in all chats (private, groups, channels). For userbots, this is typically what you want.

Resuming from a Session String ​

Once you have a session string, you can skip the authentication flow entirely:

go
package main

import (
    "context"
    "fmt"
    "log"

    tg "github.com/mtgo-labs/mtgo/telegram"
)

func main() {
    sessionStr := "your_session_string_here"

    client, err := tg.NewClient(apiID, apiHash, &tg.Config{
        SessionString: sessionStr,
        SavePeers:     true,
        SessionName:   "resumed_userbot",
    })
    if err != nil {
        log.Fatal(err)
    }

    if err := client.Connect(0); err != nil {
        log.Fatal(err)
    }
    defer client.Stop()

    me, _ := client.GetMe(context.Background())
    fmt.Printf("Resumed session as @%s\n", me.Username)

    client.Idle()
}

Running ​

bash
export API_ID=12345
export API_HASH=your_api_hash
export PHONE=+1234567890

go run main.go

On first run, you'll be prompted for the SMS code (and optionally 2FA password). On subsequent runs, the saved session is used automatically.

WARNING

Userbots must comply with Telegram's Terms of Service. Avoid spamming, scraping, or automating actions that violate user privacy.

Released under the Apache-2.0 License.