Получение информации с LeetCode о пользователе на Golang
- понедельник, 10 июня 2024 г. в 00:00:07
 
LeetCode - популярная платформа для подготовки к собеседованиям по программированию, предоставляющая задачи на алгоритмы и структуры данных. Чтобы улучшить свои навыки и изучить свои успехи, пользователи могут хотеть получить информацию о своем профиле на LeetCode, такую как решенные задачи, статистика по времени и другие данные.
В данной статье будет рассмотрено, как можно написать программу на Golang для получения информации о пользователе с помощью API LeetCode. Для разработки будет использоваться библиотека graphql на Golang, чтобы отправить запросы к API LeetCode и получить необходимые данные о пользователе. Для простоты взаимодействия с пользователями будет использован Telegram API. Стоит добавить, что для Телеграм бота не нужно покупать отдельный хостинг, можно все сделать локально, нужно только доступ к интернету.
Выбор между Golang и Python для разработки ботов зависит от конкретных требований проекта, но есть несколько аспектов, в которых Golang может оказаться более предпочтительным выбором по сравнению с Python:
Производительность: Golang обычно обладает более высокой производительностью и эффективностью исполнения кода по сравнению с Python. Это может быть критично для ботов, которые должны обрабатывать большие объемы данных или выполнять сложные вычисления.
Кроссплатформенность: Golang поддерживает кроссплатформенность, что означает, что боты, разработанные на Golang, могут легко работать на различных операционных системах без необходимости внесения изменений в код.
Статическая типизация: Golang является языком программирования со статической типизацией, что может помочь обнаружить ошибки на ранних этапах разработки и сделать код более надежным.
Многопоточность: Golang имеет встроенную поддержку горутин, которые облегчают параллельное выполнение операций, что особенно полезно для ботов, работающих с большим количеством одновременных запросов.
Эффективность работы с конкурентностью: Golang имеет механизмы управления конкурентностью, такие как каналы (channels) и слабые блокировки (mutexes), что облегчает создание многозадачных ботов.
Хотя Python также является популярным и мощным языком программирования для разработки ботов благодаря своей легкости и простоте в использовании, Golang может быть предпочтительным выбором, особенно если важны производительность, эффективность и работа с высокой нагрузкой.
Перед тем как начать разрабатывать необходимо установить необходимые для работы библиотеки.
go install -v github.com/go-telegram-bot-api/telegram-bot-api/v5
go install -v github.com/machinebox/graphqlДля удобства поддержки и читаемости необходимо организовать код, храня различную функциональность в отдельных файлах. Начнем с того, что есть некая точка входа main.go через которую запускается бот:
package main
import (
	"log"
)
func check(err error) {
	if err != nil {
		log.Println(err)
	}
}
func main() {
	startBot()
}После точки входа следует создать файл telegram.go, в котором будет происходить логика работы Телеграм бота, такая как: получение информации от пользователя, отправка ответа пользователю, отправка инструкции использования пользователю:
package main
import (
	"strconv"
	botAPI "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
func isCallbackQuery(update *botAPI.Update) bool {
	return update.CallbackQuery != nil && update.CallbackQuery.Message.Text != ""
}
func isStartMessage(update *botAPI.Update) bool {
	return update.Message != nil && update.Message.Text == "/start"
}
func getKeyboardRow(buttonText string, buttonCode string) []botAPI.InlineKeyboardButton {
	return botAPI.NewInlineKeyboardRow(botAPI.NewInlineKeyboardButtonData(buttonText, buttonCode))
}
func renderingMenu(bot *botAPI.BotAPI, chatId int64) {
	msg := botAPI.NewMessage(chatId, "Select action")
	msg.ReplyMarkup = botAPI.NewInlineKeyboardMarkup(
		getKeyboardRow(left+" "+"Information about bot"+" "+right, "info"),
	)
	bot.Send(msg)
}
func updateProccessing(update *botAPI.Update, bot *botAPI.BotAPI) {
	var message string
	choice := update.CallbackQuery.Data
	if choice == "info" {
		message = "My functionality: \n Get username information from leetcode. You only need to send the username in a message and get the result immediately"
	} 
	msg := botAPI.NewMessage(update.CallbackQuery.From.ID, message)
	bot.Send(msg)
}
func startBot() {
	var message string
	bot, err := botAPI.NewBotAPI(token)
	check(err)
	updateConfig := botAPI.NewUpdate(0)
	updateConfig.Timeout = timeout
	updates := bot.GetUpdatesChan(updateConfig)
	for update := range updates {
		if isCallbackQuery(&update) {
			updateProccessing(&update, bot)
		} else {
			if isStartMessage(&update) {
				message = "Hi everyone! \nIt's a telegram bot. You can get information from leetcode by username. If you need more information than click info."
				renderingMenu(bot, update.Message.Chat.ID)
			} else {
				username := getUsersInfo(update.Message.Text)
				if username.MatchedUser.Username == "" {
					message = cancel+"Information: \nusername:  user not found" 
				} else {
					message = "Information: \nusername: " + username.MatchedUser.Username
					message += "\n" + solved + "solved: " + strconv.Itoa(username.MatchedUser.SubmitStats.AcSubmissionNum[0].Count) + " / " +strconv.Itoa(username.AllQuestionsCount[0].Count)
					message += "\n" + easy + "Easy: " + strconv.Itoa(username.MatchedUser.SubmitStats.AcSubmissionNum[1].Count) + " / " + strconv.Itoa(username.AllQuestionsCount[1].Count)
					message += "\n" + middle + "Middle: " + strconv.Itoa(username.MatchedUser.SubmitStats.AcSubmissionNum[2].Count) + " / " + strconv.Itoa(username.AllQuestionsCount[2].Count)
					message += "\n" + hard + "Hard: " + strconv.Itoa(username.MatchedUser.SubmitStats.AcSubmissionNum[3].Count) + " / " + strconv.Itoa(username.AllQuestionsCount[3].Count)
				}
			}
			msg := botAPI.NewMessage(update.Message.Chat.ID, message)
			msg.ReplyToMessageID = update.Message.MessageID
			bot.Send(msg)
		}
	}
}Следующая функциональность бота - работа с graphql. Для удобства использования были созданы структуры и определены к ним поля, которые используются в graphql запросах и вынесено в отдельный файл model.go:
package main
type Submission struct {
	Count      int    `json:"count"`
	Difficulty string `json:"difficulty"`
}
type UserProfileData struct {
	MatchedUser       MatchedUser  `json:"matchedUser"`
	AllQuestionsCount []Submission `json:"allQuestionsCount"`
}
type SubmitStats struct {
	AcSubmissionNum []Submission `json:"acSubmissionNum"`
}
type MatchedUser struct {
	Username    string      `json: username`
	SubmitStats SubmitStats `json:"submitStats"`
}Для общения с API LeetCode был создан graphql.go. Для простоты использования были созданы функции, которые возвращают graphql запросы. Основная функция осуществляет запрос к API и возвращает структуру, в которой хранится информация о пользователе: имя пользователя, сколько всего решил задач и сколько по каждому уровню сложности было решено:
package main
import (
	"context"
	"github.com/machinebox/graphql"
)
func getQueryUserInfo() string {
	return `query ($username: String!) { matchedUser(username: $username) { 
				username
				submitStats { 
				acSubmissionNum { 
					difficulty 
					count 
					} 
				} 
			}
		}`
}
func getQueryQntyQuestions() string {
	return `{ allQuestionsCount { 
			difficulty 
			count 
			}
		}`
}
func getUsersInfo(username string) UserProfileData {
	var requestUser UserProfileData
	client := graphql.NewClient("https://leetcode.com/graphql")
	query := getQueryQntyQuestions()
	request := graphql.NewRequest(query)
	ctx := context.Background()
	err := client.Run(ctx, request, &requestUser)
	check(err)
	query = getQueryUserInfo()
	request = graphql.NewRequest(query)
	request.Var("username", username)
	err = client.Run(ctx, request, &requestUser)
	if err != nil {
		check(err)
	}
	return requestUser
}Для хранения константных информаций: токен, таймаут, эмодзи, если нужно, создан constants.go:
package main
const (
	token   = "Your Token"
	left    = "\U000025B6"
	right   = "\U000025C0"
	easy    = "\U0001F4D7"
	hard    = "\U0001F4D5"
	middle  = "\U0001F4D9"
	solved	= "\U00002705"
	cancel	= "\U0001F6AB"
	timeout = 5
)Предстоит протестировать бот. Для проверки будет взят username автора статьи и несуществующий пользователь(к существующему пользователю допишется цифра). Для начала предстоит проверка на существующем пользователе. Была получена данная информация, да эта информация верна, вот пользователь на LeetCode.



А теперь проверим на несуществующем. Бот ответил, что такого пользователя нет.


Получение информации о пользователе с платформы LeetCode на языке программирования Golang может быть важным и полезным шагом для разработчиков, желающих улучшить свои навыки в алгоритмах и структурах данных. Используя API LeetCode, можно получить доступ к различным данным о пользователе.