Чат вконтакта в качестве терминала

в 18:04, , рубрики: Go, golang, vkonakte, Вконтакте, Вконтакте API, КодоБред, ненормальное программирование

Вдохновлённый постом «Простой диспетчер задач с веб-интерфейсом, написанный на языке GO для Unix-систем включая Android», языком Go и утилитой simple status, я решил написать в качестве забавного эксперимента чат-бота для социальной сети Vkontakte со схожим функционалом.

Почему выбор пал на чат-бота и социальную сеть? Кроме очевидного just for fun имеются и практические выкладки:

  • Не нужны дополнительные телодвижения для доступа к приложению, запущенном на компьютере с динамическим ip или находящимся за роутером.
  • Доступ к Вконтактике есть практически везде – достаточно иметь при себе телефон с доступом в интернет.
  • Вопрос о реализации аутентификации на уровне конечного приложения практически отпадает.


Утилита представляет собой простого чат бота, реагирующего на текстовые команды. Пишем в VK сообщение самому себе, утилита с некоторой периодичностью опрашивает VK API и получает список сообщений. Дальше только остаётся сопоставить введённое сообщение со списком предопределённых текстовых команд.
Ничего сложного, правда есть некоторые нюансы, которые надо знать про VK API.
Во-первых, для доступа к личным сообщениям надо регистрировать VK приложение как desktop. И при запросе access token выставить права на доступ к личным сообщениям и «бланковую» страницу для callback.
Адрес для получения access token будет выглядеть примерно так:

https://oauth.vk.com/authorize?client_id=#####&scope=offline,messages&redirect_uri=https%3A%2F%2Foauth.vk.com%2Fblank.html&display=page&v=5.28&response_type=token

Во-вторых, есть ограничение по количеству запросов в секунду, поэтому получение сообщений происходит с таймером в 2 секунды.

	c := time.Tick(2 * time.Second)
	lastMsgId := int64(0)
	for _ = range c {
		msgs, err := getMsgs(lastMsgId)
		if err != nil {
			fmt.Println(err)
		} else {
			if len(msgs.Response.Items) > 0 {
				lastMsgId = msgs.Response.Items[0].Id
			}
			for _, msg := range msgs.Response.Items {
				msgBody := strings.Trim(msg.Body, "")
				if checkVkId(msg.UserId) && checkTime(msg.Date) && checkResultPrefix(msgBody) {
					go doCmd(msgBody)
				}
			}
		}
	}

Разбор входящего сообщения и отправка результата исполнения команды:

func doSysCmd(msg string) {
	switch true {
	case strings.HasPrefix(msg, "@sys/host"):
		sendMsg(*vk_id, fmt.Sprintf("%+v", sysstat.GetHost()))
	case strings.HasPrefix(msg, "@sys/disk"):
		sendMsg(*vk_id, fmt.Sprintf("%+v", sysstat.GetDisk("/")))
	case strings.HasPrefix(msg, "@sys/load"):
		sendMsg(*vk_id, fmt.Sprintf("%+v", sysstat.GetLoad()))
	case strings.HasPrefix(msg, "@sys/ram"):
		sendMsg(*vk_id, fmt.Sprintf("%+v", sysstat.GetRam()))
	case strings.HasPrefix(msg, "@sys"):
		sendMsg(*vk_id, fmt.Sprintf("n%+v", sysstat.GetSystem("/")))
	case strings.HasPrefix(msg, "@sh"):
		args := strings.SplitN(msg, " ", 2)
		if len(args) < 2 {
			return
		}
		fmt.Println("exec: ", args[1])
		sendMsg(*vk_id, fmt.Sprintf(" %+v", sysstat.ExecShell(args[1])))
	}
}

Кроме получения текущего статуса системы в качестве бонуса можно сохранять произвольные заметки командами:

!list – вывод сохраненного списка с номерами id
!add [текст] – добавить новую запись
!del [номер] – удалить запись с указанным id

Заметки сохраняются исключительно в памяти.
Защита от краха приложения не предусмотрена, я взял за практику запускать подобные вещи под супервизором, который сам перезапускает приложение, если возникнет критическая ошибка. Потребление ОЗУ для go программ традиционно минимально и колеблется в районе 10 МБ, ещё столько же отнимает супервизор.

Исходники на github.

Автор: ZurgInq

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js