Привіт, народ!

Нещодавно у нас була вільна хвилина, і ми зробили чат-бота. Він знає, як вести переписку з вами, а також шукати відповіді на ваші запитання у Вікіпедії. Ми використовували Python для його реалізації.

По-перше, давайте встановимо всі необхідні бібліотеки. Їх три: pyTelegramBotAPI, scikit-learn і Вікіпедія. Вони встановлюються просто:

pip install pyTelegramBotAPI
pip install Wikipedia
pip install scikit-learn

Після установки всіх бібліотек ми починаємо розробку. Для початку ми імпортуємо всі бібліотеки, встановимо мову для Вікіпедії та підключимо телеграм-бот:

import telebot, wikipedia, re
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression

wikipedia.set_lang("ua")
bot = telebot.TeleBot('Ваш ключ, отриманий від BotFather')

Тепер давайте напишемо код, щоб очистити всі непотрібні символи, які вводить користувач:

def clean_str(r):
	r = r.lower()
	r = [c for c in r if c in alphabet]
	return ''.join(r)

alphabet = ' 1234567890-йцукенгшщзхїфівапролджєґячсмитьбюqwertyuiopasdfghjklzxcvbnm?%.,()!:;'

Також потрібно створити діалог.txt файл в папці, де знаходиться ваш код, в якому ми будемо створювати репліки, на які бот повинен відповідати. Ось приклад цього файлу:

привіт\доброго дня!
як справи\добре.
хто ти\я Борис.

Рядок перед “\” означає питання користувача, а потім відповідь нашого бота. Тоді давайте запишемо цей код у наш файл бота:

def update():
	with open('dialogues.txt', encoding='utf-8') as f:
		content = f.read()
	
	blocks = content.split('\n')
	dataset = []
	
	for block in blocks:
		replicas = block.split('\\')[:2]
		if len(replicas) == 2:
			pair = [clean_str(replicas[0]), clean_str(replicas[1])]
			if pair[0] and pair[1]:
				dataset.append(pair)
	
	X_text = []
	y = []
	
	for question, answer in dataset[:10000]:
		X_text.append(question)
		y += [answer]
	
	global vectorizer
	vectorizer = CountVectorizer()
	X = vectorizer.fit_transform(X_text)
	
	global clf
	clf = LogisticRegression()
	clf.fit(X, y)

update()

Цей фрагмент коду зчитує файл діалогів.txt, потім перетворює репліки в так звані вектори, за допомогою яких наш бот буде шукати найбільш підходящу відповідь на поставлене нами питання. Наприклад, якщо ви написали в файлі dialogues.txt питання “Ви знаєте Аню”, а відповідь на нього – “Так, звичайно”, то бот також відповість на аналогічні питання, наприклад, “Ви знаєте Васю”.

Тепер давайте напишемо фрагмент коду, який буде генерувати векторні відповіді:

def get_generative_replica(text):
	text_vector = vectorizer.transform([text]).toarray()[0]
	question = clf.predict([text_vector])[0]
	return question

Цей фрагмент коду бере запитання від користувача і повертає відповідь від бота.

Тепер давайте напишемо функцію пошуку інформації у Вікіпедії:

def getwiki(s):
    try:
        ny = wikipedia.page(s)
        wikitext=ny.content[:1000]
        wikimas=wikitext.split('.')
        wikimas = wikimas[:-1]
        wikitext2 = ''
        for x in wikimas:
            if not('==' in x):
                if(len((x.strip()))>3):
                   wikitext2=wikitext2+x+'.'
            else:
                break
        wikitext2=re.sub('\([^()]*\)', '', wikitext2)
        wikitext2=re.sub('\([^()]*\)', '', wikitext2)
        wikitext2=re.sub('\{[^\{\}]*\}', '', wikitext2)
        return wikitext2
    except Exception as e:
        return 'В Вікіпедії немає інформации про це'

Цей фрагмент коду отримує питання користувача, потім шукає відповідь на нього у Вікіпедії і якщо відповідь знайдена, він дає її користувачеві, а якщо відповідь не знайдена, він пише, що «В Вікіпедії не має інформації про це».

Тепер напишіть останній фрагмент коду:

@bot.message_handler(commands=['start'])
def start_message(message):
	bot.send_message(message.chat.id,"Доброго дня, Сєр.")

question = ""

@bot.message_handler(content_types=['text'])
def get_text_messages(message):
	command = message.text.lower()
	if command =="неправильно":
		bot.send_message(message.from_user.id, "а як?")
		bot.register_next_step_handler(message, wrong)
	else:
		global question
		question = command
		reply = get_generative_replica(command)
		if reply=="wiki":
			bot.send_message(message.from_user.id, getwiki(command))
		else:
			bot.send_message(message.from_user.id, reply)

def wrong(message):
	a = f"{question}\{message.text.lower()} \n"
	with open('dialogues.txt', "a", encoding='utf-8') as f:
		f.write(a)
	bot.send_message(message.from_user.id, "Готово")
	update()

У цій частині телеграм-коду бот відповідає на нього при отриманні повідомлення від користувача і якщо відповідь неправильна, то користувач пише «неправильно». Якщо бот отримує повідомлення «неправильно», він бере останнє питання користувача і задає «а як?», після чого користувач повинен відправити йому правильну відповідь. Після цього бот оновлює свою базу запитань і відповідей і правильно відповідає на них на наступні питання користувача. І якщо боту довелося взяти відповідь на питання з Вікіпедії, то користувач у відповідь на питання «а як?», повинен написати «wiki». Залишається в кінці віднести рядок:

bot.polling(none_stop=True)

І ви можете запустити і протестувати бота.

Викладаємо весь код файлу бота нижче:

import telebot, wikipedia, re
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression

bot = telebot.TeleBot('Ваш ключ отриманий від BotFather')

wikipedia.set_lang("ua")

def clean_str(r):
	r = r.lower()
	r = [c for c in r if c in alphabet]
	return ''.join(r)

alphabet = ' 1234567890-йцукенгшщзхїфівапролджєґячсмитьбюqwertyuiopasdfghjklzxcvbnm?%.,()!:;'

def update():
	with open('dialogues.txt', encoding='utf-8') as f:
		content = f.read()
	
	blocks = content.split('\n')
	dataset = []
	
	for block in blocks:
		replicas = block.split('\\')[:2]
		if len(replicas) == 2:
			pair = [clean_str(replicas[0]), clean_str(replicas[1])]
			if pair[0] and pair[1]:
				dataset.append(pair)
	
	X_text = []
	y = []
	
	for question, answer in dataset[:10000]:
		X_text.append(question)
		y += [answer]
	
	global vectorizer
	vectorizer = CountVectorizer()
	X = vectorizer.fit_transform(X_text)
	
	global clf
	clf = LogisticRegression()
	clf.fit(X, y)

update()

def get_generative_replica(text):
	text_vector = vectorizer.transform([text]).toarray()[0]
	question = clf.predict([text_vector])[0]
	return question

def getwiki(s):
    try:
        ny = wikipedia.page(s)
        wikitext=ny.content[:1000]
        wikimas=wikitext.split('.')
        wikimas = wikimas[:-1]
        wikitext2 = ''
        for x in wikimas:
            if not('==' in x):
                if(len((x.strip()))>3):
                   wikitext2=wikitext2+x+'.'
            else:
                break
        wikitext2=re.sub('\([^()]*\)', '', wikitext2)
        wikitext2=re.sub('\([^()]*\)', '', wikitext2)
        wikitext2=re.sub('\{[^\{\}]*\}', '', wikitext2)
        return wikitext2
    except Exception as e:
        return 'В Вікіпедії немає інформации про це'

@bot.message_handler(commands=['start'])
def start_message(message):
	bot.send_message(message.chat.id,"Доброго дня, Сєр.")

question = ""

@bot.message_handler(content_types=['text'])
def get_text_messages(message):
	command = message.text.lower()
	if command =="неправильно":
		bot.send_message(message.from_user.id, "а як?")
		bot.register_next_step_handler(message, wrong)
	else:
		global question
		question = command
		reply = get_generative_replica(command)
		if reply=="wiki":
			bot.send_message(message.from_user.id, getwiki(command))
		else:
			bot.send_message(message.from_user.id, reply)

def wrong(message):
	a = f"{question}\{message.text.lower()} \n"
	with open('dialogues.txt', "a", encoding='utf-8') as f:
		f.write(a)
	bot.send_message(message.from_user.id, "Готово")
	update()

bot.polling(none_stop=True)

Сподіваюся, вам сподобалася стаття 🙂