Skip to content

Commit

Permalink
add: ping and improve
Browse files Browse the repository at this point in the history
  • Loading branch information
AshokShau committed Sep 30, 2024
1 parent e31e96f commit 5bcbbb5
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 15 deletions.
41 changes: 26 additions & 15 deletions Telegram/modules/inline.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package modules
import (
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net/http"

"regexp"
"strconv"
"strings"
Expand All @@ -16,7 +18,10 @@ import (
"github.com/PaulSonOfLars/gotgbot/v2/ext"
)

const maxMessageLength = 4096 // Telegram's maximum message length
const (
maxMessageLength = 4096 // Telegram's maximum message length
apiURL = "https://github.com/PaulSonOfLars/telegram-bot-api-spec/raw/main/api.json"
)

// apiCache is a global cache for storing API methods and types.
var apiCache struct {
Expand All @@ -25,7 +30,6 @@ var apiCache struct {
Types map[string]Type
}

// Method Type, Field structs remain unchanged
type Method struct {
Name string `json:"name"`
Description []string `json:"description"`
Expand All @@ -51,11 +55,13 @@ type Field struct {
// fetchAPI fetches the API documentation from a remote source and updates the apiCache.
func fetchAPI() error {
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get("https://github.com/PaulSonOfLars/telegram-bot-api-spec/raw/main/api.json")
resp, err := client.Get(apiURL)
if err != nil {
return fmt.Errorf("failed to fetch API: %w", err)
}
defer resp.Body.Close()
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)

var apiDocs struct {
Methods map[string]Method `json:"methods"`
Expand All @@ -67,9 +73,9 @@ func fetchAPI() error {
}

apiCache.Lock()
defer apiCache.Unlock()
apiCache.Methods = apiDocs.Methods
apiCache.Types = apiDocs.Types
apiCache.Unlock()

return nil
}
Expand All @@ -86,6 +92,13 @@ func StartAPICacheUpdater(interval time.Duration) {
}()
}

// getAPICache returns a snapshot of the current API cache.
func getAPICache() (map[string]Method, map[string]Type) {
apiCache.RLock()
defer apiCache.RUnlock()
return apiCache.Methods, apiCache.Types
}

// inlineQueryHandler handles inline queries from the bot.
func inlineQueryHandler(bot *gotgbot.Bot, ctx *ext.Context) error {
query := strings.TrimSpace(ctx.InlineQuery.Query)
Expand All @@ -100,11 +113,7 @@ func inlineQueryHandler(bot *gotgbot.Bot, ctx *ext.Context) error {
query = kueri
}

apiCache.RLock()
methods := apiCache.Methods
types := apiCache.Types
apiCache.RUnlock()

methods, types := getAPICache()
results := searchAPI(query, methods, types)

if len(results) == 0 {
Expand Down Expand Up @@ -135,19 +144,20 @@ func sendEmptyQueryResponse(bot *gotgbot.Bot, ctx *ext.Context) error {
// searchAPI searches the API methods and types for the given query.
func searchAPI(query string, methods map[string]Method, types map[string]Type) []gotgbot.InlineQueryResult {
var results []gotgbot.InlineQueryResult
lowerQuery := strings.ToLower(query)

search := func(name string, href string, msg string) {
search := func(name, href, msg string) {
results = append(results, createInlineResult(name, href, msg, href))
}

for name, method := range methods {
if strings.Contains(strings.ToLower(name), strings.ToLower(query)) {
if strings.Contains(strings.ToLower(name), lowerQuery) {
search(name, method.Href, buildMethodMessage(method))
}
}

for name, typ := range types {
if strings.Contains(strings.ToLower(name), strings.ToLower(query)) {
if strings.Contains(strings.ToLower(name), lowerQuery) {
search(name, typ.Href, buildTypeMessage(typ))
}
}
Expand All @@ -159,7 +169,7 @@ func searchAPI(query string, methods map[string]Method, types map[string]Type) [
func sendNoResultsResponse(bot *gotgbot.Bot, ctx *ext.Context, query string) error {
_, err := ctx.InlineQuery.Answer(bot, []gotgbot.InlineQueryResult{noResultsArticle(query)}, &gotgbot.AnswerInlineQueryOpts{
IsPersonal: true,
CacheTime: 5,
CacheTime: 500,
})
return err
}
Expand Down Expand Up @@ -194,6 +204,7 @@ func buildMessage(name string, description []string, returns []string, fields []
if len(message) > maxMessageLength {
return fmt.Sprintf("See full documentation: %s", href)
}

return message
}

Expand Down Expand Up @@ -237,7 +248,7 @@ func noResultsArticle(query string) gotgbot.InlineQueryResult {
}
}

// sanitizeHTML removes unsupported HTML tags from the message
// sanitizeHTML removes unsupported HTML tags from the message.
func sanitizeHTML(input string) string {
re := regexp.MustCompile(`<[^>]*>`)
return re.ReplaceAllString(input, "")
Expand Down
1 change: 1 addition & 0 deletions Telegram/modules/loadModules.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ func newDispatcher() *ext.Dispatcher {

func loadModules(d *ext.Dispatcher) {
d.AddHandler(handlers.NewCommand("start", start))
d.AddHandler(handlers.NewCommand("ping", ping))
d.AddHandler(handlers.NewInlineQuery(inlinequery.All, inlineQueryHandler))
}
68 changes: 68 additions & 0 deletions Telegram/modules/misc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package modules

import (
"fmt"
"strings"
"time"
)

// getFormattedDuration returns a formatted string representing the duration
func getFormattedDuration(diff time.Duration) string {
seconds := int(diff.Seconds())
minutes := seconds / 60
hours := minutes / 60
days := hours / 24
weeks := days / 7
months := days / 30

// Calculate remaining values after each unit is accounted for
remainingSeconds := seconds % 60
remainingMinutes := minutes % 60
remainingHours := hours % 24
remainingDays := days % 7

var text string

// Format months
if months != 0 {
text += fmt.Sprintf("%d months ", months)
}

// Format weeks
if weeks != 0 {
text += fmt.Sprintf("%d weeks ", weeks)
}

// Format days
if remainingDays != 0 {
text += fmt.Sprintf("%d days ", remainingDays)
}

// Format hours
if remainingHours != 0 {
text += fmt.Sprintf("%d hours ", remainingHours)
}

// Format minutes
if remainingMinutes != 0 {
text += fmt.Sprintf("%d minutes ", remainingMinutes)
}

// Format seconds
if remainingSeconds != 0 || text == "" { // Include seconds if there's no larger unit or if there are remaining seconds
text += fmt.Sprintf("%d seconds", remainingSeconds)
}

// Trim any trailing space
text = trimSuffix(text, " ")

return text
}

// trimSuffix removes the suffix from the string if it exists
func trimSuffix(s, suffix string) string {
if strings.HasSuffix(s, suffix) {
return s[:len(s)-len(suffix)]
}
return s
}
31 changes: 31 additions & 0 deletions Telegram/modules/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import (
"github.com/PaulSonOfLars/gotgbot/v2"
"github.com/PaulSonOfLars/gotgbot/v2/ext"
"log"
"time"
_ "time/tzdata"
)

var StartTime = time.Now()

func start(b *gotgbot.Bot, ctx *ext.Context) error {
msg := ctx.EffectiveMessage
text := fmt.Sprintf("👋 Hello! I'm your handy Telegram Bot API assistant, built with GoTgBot.\n\n💡 Usage: <code>@%s your_query</code> - Quickly search for any method or type in the Telegram Bot API documentation.", b.User.Username)
Expand All @@ -19,3 +23,30 @@ func start(b *gotgbot.Bot, ctx *ext.Context) error {

return ext.EndGroups
}

func ping(b *gotgbot.Bot, ctx *ext.Context) error {
msg := ctx.EffectiveMessage
startTime := time.Now()

rest, err := msg.Reply(b, "<code>Pinging</code>", &gotgbot.SendMessageOpts{ParseMode: "HTML"})
if err != nil {
return fmt.Errorf("ping: %v", err)
}

// Calculate latency
elapsedTime := time.Since(startTime)

// Calculate uptime
uptime := time.Since(StartTime)
formattedUptime := getFormattedDuration(uptime)

location, _ := time.LoadLocation("Asia/Kolkata")
responseText := fmt.Sprintf("Pinged in %vms (Latency: %.2fs) at %s\n\nUptime: %s", elapsedTime.Milliseconds(), elapsedTime.Seconds(), time.Now().In(location).Format(time.RFC1123), formattedUptime)

_, _, err = rest.EditText(b, responseText, nil)
if err != nil {
return fmt.Errorf("ping: %v", err)
}

return ext.EndGroups
}

0 comments on commit 5bcbbb5

Please sign in to comment.