#!/usr/lib/python3 python
# -*- coding: utf-8 -*-
import os
import sys
import config
import db
import random
import datetime
import vk_api
import telebot
import threading
import urllib.request as ur
from PIL import Image # Для преобразования изображений из webp в PNG

config.initConfig()

module = sys.modules[__name__]

# Код настоятельно рекомендуется читать снизу вверх!

#    _______        _     
#   |__   __|      | |    
#      | | ___  ___| |__  
#      | |/ _ \/ __| '_ \ 
#      | |  __/ (__| | | |
#      |_|\___|\___|_| |_|
#                         
#   Технические функции

# Получаем текущее время
def current_time():
	delta = datetime.timedelta( hours=3 )
	utc = datetime.timezone.utc
	fmt = '%H:%M:%S'
	time = ( datetime.datetime.now(utc) + delta )
	timestr = time.strftime(fmt)
	return timestr

# Получение имени пользователя
def getUserName( msg ):
	# Для приёма личных сообщений когда пишут через группу
	if ( int( msg.get('from_id') ) < 0 ):
		return None
	else:
		dataname = module.vk.users.get( user_ids = msg.get('from_id') )
		name = str ( dataname[0]['first_name'] + ' ' + dataname[0]['last_name'] )
	return name

def getUserTName( msg ):
	if msg.last_name is None:
		userName = str( msg.first_name )
	else:
		userName = str( msg.first_name + " " + msg.last_name )
	return userName

# Проверка на наличие аттачментов в сообщении
def checkAttachments( msg, idd ):
	if not( msg.get( 'attachments' ) ):
		return False
	transferAttachmentsToTelegram( idd, getAttachments( msg ) )
	return True

# Получаем аттачменты из сообщения ВК
def getAttachments( msg ):

	attachList = []

	for att in msg['attachments'][0:]:

		attType = att.get( 'type' )

		attachment = att[attType]

		if attType == 'photo': # Проверка на тип фотографии

			for photoType in attachment.get('sizes')[0:]:
				if photoType.get('type') == 'x': # <=604x604
					attachments = photoType.get('url')
				if photoType.get('type') == 'y': # >605x605
					attachments = photoType.get('url')
				if photoType.get('type') == 'z': # <=1280x720
					attachments = photoType.get('url')
				if photoType.get('type') == 'w':# >1280x720
					attachments = photoType.get('url') # <=2560x1440
					attType = 'other'

		elif attType == 'doc': # Проверка на тип документа:
		# Про типы документов можно узнать тут: https://vk.com/dev/objects/doc
			docType = attachment.get( 'type' )
			if docType != 3 and docType != 4 and docType != 5:
				attType = 'other'
			if attachment.get( 'url' ):
				attachments = attachment.get( 'url' )

		elif attType == 'sticker': # Проверка на стикеры:
			for sticker in attachment.get( 'images' )[0:]:
				# Можно 256 или 512, но будет слишком огромная пикча
				if sticker.get('width') == 128:
					attachments = sticker.get( 'url' )

		elif attType == 'audio':
			attachments = str ( '𝅘𝅥𝅮 ' + attachment.get('artist') + ' - ' + 
				attachment.get('title') + ' 𝅘𝅥𝅮' )
			attType = 'other'

		elif attType == 'audio_message':
			attachments = attachment.get('link_ogg')

		elif attType == 'video':

			ownerId = str( attachment.get( 'owner_id' ) )
			videoId = str( attachment.get( 'id' ) )
			accesskey = str( attachment.get( 'access_key' ) )

			fullURL = str( ownerId + '_' + videoId + '_' + accesskey)

			attachments = module.vk.video.get(videos = fullURL )['items'][0].get('player')

		# Неизвестный тип?
		else:

			attachments = None

		attachList.append( { 	'type':attType,
								'link':attachments } )

	#print( attachList )

	return attachList

# Проверка чата ВК на различные события
def checkEvents( msg, chatid ):

	if not ( msg['last_message'].get( 'action' ) ):
		return None # И так сойдёт

	event = msg['last_message']['action'].get( 'type' )
	userName = getUserName( msg['last_message'] )

	# Ниже проверям наш чат на различные события
	# См. https://vk.com/dev/objects/message

	if event == 'chat_title_update':
		eObject = str( msg['last_message']['action'].get( 'text' ) )
		mbody = " *** " + userName + " изменил(а) название беседы на " + eObject + " ***"

	elif event == 'chat_invite_user':
		dataname = module.vk.users.get( user_ids = msg['last_message']['action'].get( 'member_id' ) )
		eObject = str ( dataname[0]['first_name'] + ' ' + dataname[0]['last_name'] )
		mbody = " *** " + userName + " пригласил(а) в беседу " + eObject + " ***"

	elif event == 'chat_kick_user':
		dataname = module.vk.users.get( user_ids = msg['last_message']['action'].get( 'member_id' ) )
		eObject = str ( dataname[0]['first_name'] + ' ' + dataname[0]['last_name'] )
		mbody = " *** " + userName + " кикнул(а) из беседы " + eObject + " ***"

	elif event == 'chat_photo_update':
		mbody = " *** " + userName + " обновил(а) фото беседы: ***"

	elif event == 'chat_photo_remove':
		mbody = " *** " + userName + " удалил(а) фото беседы! ***"

	elif event == 'chat_pin_message':
		eObject = str( msg['last_message']['action'].get( 'message' ) )
		if( eObject ):
			mbody = " *** " + userName + " закрепил(а): " + eObject + " ***"
		else:
			mbody = " *** " + userName + " закрепил(а) сообщение! ***"

	elif event == 'chat_unpin_message':
		mbody = " *** " + userName + " открепил(а) сообщение! ***"

	elif event == 'chat_create':
		print( 'Беседа была создана!' )

	else:
		return None

	transferMessagesToTelegram( chatid, None, mbody, None )

# Проверка на наличие перешлённых сообщений
def getFwdMessages( msg, idd ):

	if not( msg.get( 'fwd_messages' ) ):
		return None # И так сойдёт

	fwdList = []
	fwdMsg = msg.get( 'fwd_messages' )

	while not fwdMsg is None:

		userName = getUserName( fwdMsg[0] )

		fwdList.append( { 'body':fwdMsg[0].get( 'text' ), 'userName':userName } )

		checkAttachments( fwdMsg[0], idd )

		fwdMsg = fwdMsg[0].get( 'fwd_messages' )

	#print( fwdList )

	return fwdList

#    _____          _ _               _       
#   |  __ \        | (_)             | |      
#   | |__) |___  __| |_ _ __ ___  ___| |_ ___ 
#   |  _  // _ \/ _` | | '__/ _ \/ __| __/ __|
#   | | \ \  __/ (_| | | | |  __/ (__| |_\__ \
#   |_|  \_\___|\__,_|_|_|  \___|\___|\__|___/
#                                             
#  Функции, принимающие и отправляющие сообщения ВК <==> Telegram

def checkRedirect_vk( msg ):

	chatid = str( msg['conversation']['peer']['local_id'] )

	# Проверка на существование переадресации в конфиге
	if not config.getCell( "vk_" + chatid ) is None:

		forwardMessage = getFwdMessages( msg['last_message'], chatid )

		userName = getUserName( msg['last_message'] )
		mbody = msg['last_message'].get( 'text' )

		# Чтобы при событии не посылалось пустое сообщение
		if checkEvents( msg, chatid ) is None:
			transferMessagesToTelegram( chatid, userName, mbody, forwardMessage )

		# Проверка на аттачменты, пересланные сообщения, видео...
		# Проверка сделана, чтобы исключить повтор картинки
		if forwardMessage is None:
			checkAttachments( msg['last_message'], chatid )

		return False

def transferMessageToVK( chatid, text, fromUser, attachment ):

	if ( config.getCell('telegram_SendName') ):
		time = current_time()
		text = str( time + ' | ' + fromUser + ': ' + text )

	randid = random.randint(-9223372036854775808, +9223372036854775807) #int64

	if attachment is None:

		try:
			module.vk.messages.send( chat_id = config.getCell( 't_' + chatid ), message = text, random_id=randid )
		except vk_api.ApiError as error_msg:
			module.vk.messages.send( user_id = config.getCell( 't_' + chatid ), message = text, random_id=randid )
		#print( 'Сообщение успешно отправлено! ( ' + text + ' )' )

	else:

		getSticker = db.checkSticker( attachment )

		# Если стикер не найден в БД
		if getSticker is None:
			stickerURL = 'https://api.telegram.org/file/bot{0}/{1}'.format( config.getCell( 'telegram_token' ), attachment )
			saveSticker( stickerURL, attachment )
			getSticker = db.checkSticker( attachment )

		#print( getSticker )

		try:
			module.vk.messages.send( chat_id = config.getCell( 't_' + chatid ), message = "", attachment = getSticker, random_id=randid )
		except vk_api.ApiError as error_msg:
			module.vk.messages.send( user_id = config.getCell( 't_' + chatid ), message = "", attachment = getSticker, random_id=randid )

	return False

def checkRedirect_telegram( chatid, text, fromUser, attachment ):
	if not config.getCell( 't_' + chatid ) is None:
		transferMessageToVK( chatid, text, fromUser, attachment )
		return False

# Посылаем простые сообщения в Telegram
# Идея: сделать в будущем наклонные столбики, теперь главное не забыть
def transferMessagesToTelegram( idd, userName, mbody, fwdList ):

	# Условие выполняется в случае какого-либо события
	if userName is None:
		if mbody:
			module.bot.send_message( config.getCell( 'vk_' + idd ), str( mbody ) )
		return False

	time = current_time()
	niceText = str( time + ' | ' + userName + ': ' + mbody )

	if not fwdList is None:

		forwardText = ''

		for f in fwdList[0:]:
			forwardText = forwardText + str( ' | ' + f.get( 'userName' ) + ':' + ' ' + f.get( 'body' ) + ' \n\n' )

		module.bot.send_message( config.getCell( 'vk_' + idd ), niceText + '\n\n' + forwardText )

	else:
		module.bot.send_message( config.getCell( 'vk_' + idd ), niceText )

# Посылаем аттачменты в Telegram
def transferAttachmentsToTelegram ( idd, attachments ):

	for j in attachments[0:]:

		attType = j.get( 'type' )
		link = j.get( 'link' )

		if attType == 'photo' or attType == 'sticker':
			module.bot.send_photo( config.getCell( 'vk_' + idd ), link )

		elif attType == 'doc' or attType == 'gif' or attType == 'audio_message':
			module.bot.send_document( config.getCell( 'vk_' + idd ), link )

		elif attType == 'other':
			module.bot.send_message( config.getCell( 'vk_' + idd ), link )

		elif attType == 'video':

			# Потому что в ВК не может отправить полную ссылку на файл видео -_-
			module.bot.send_message( config.getCell( 'vk_' + idd ), link )

		else:
			module.bot.send_message( config.getCell( 'vk_' + idd ), '( Неизвестный тип аттачмента )' )

#   __      ___    
#   \ \    / / |   
#    \ \  / /| | __
#     \ \/ / | |/ /
#      \  /  |   < 
#       \/   |_|\_\
#                  
#

# При двухфакторной аутентификации вызывается эта функция
def auth_handler():

	key = input("Enter authentication code: ")
	# True - сохранить, False - не сохранять
	remember_device = True

	return key, remember_device

# Каптча
def captcha_handler( captcha ):
	key = input( "Enter Captcha {0}: ".format( captcha.get_url() ) ).strip()
	return captcha.try_again(key)

def init_vk():

	login = config.getCell( 'vk_login' )
	password = config.getCell( 'vk_password' )
	app = config.getCell( 'app_id' )

	print( "login in vk as: " + login )

	global vk_session

	vk_session = vk_api.VkApi( login, password, app_id=app, auth_handler=auth_handler, captcha_handler=captcha_handler )

	try:
		vk_session.auth()
	except vk_api.AuthError as error_msg:
		print( error_msg )

	module.vk = vk_session.get_api() # Важная штука

	input_vk()

def input_vk():
	
	while True:

		try:
			#Ставим онлайн боту, чому бы и нет?
			module.vk.account.setOnline()

			# Проверка на наличие подписчиков
			if ( config.getCell('vk_AddFriends') ):
				checknewfriends()

			rawMessages = module.vk.messages.getConversations( filter='unread', count=config.getCell('vk_msgForPick') )['items'][0]
			msg = rawMessages['conversation']['peer']
			module.vk.messages.markAsRead( messages_ids = msg['local_id'], peer_id = msg['id'] )

			checkRedirect_vk( rawMessages )

		# Чтобы не вылетало, а работало дальше
		except BaseException:
		#	print( 'Что-то пошло не так...' )
			continue


#    _______   _                                
#   |__   __| | |                               
#      | | ___| | ___  __ _ _ __ __ _ _ __ ___  
#      | |/ _ \ |/ _ \/ _` | '__/ _` | '_ ` _ \ 
#      | |  __/ |  __/ (_| | | | (_| | | | | | |
#      |_|\___|_|\___|\__, |_|  \__,_|_| |_| |_|
#                      __/ |                    
#                     |___/                     


def listener( messages ):
	for m in messages:

		if m.content_type == 'text':

			# На команду 'Дай ID' кидает ID чата
			if m.text == 'Дай ID':
				module.bot.send_message( m.chat.id, str( m.chat.id ) )
				continue

			checkRedirect_telegram( str( m.chat.id ), str( m.text ), getUserTName(m.from_user), None )

		elif m.content_type == 'sticker':

			if not ( config.getCell('vk_EnableStickers') ):
				return False

			filePath = module.bot.get_file( m.sticker.file_id ).file_path

			checkRedirect_telegram( str( m.chat.id ), str( m.text ), getUserTName(m.from_user), str( filePath ) )


def init_telegram():

	module.bot = telebot.TeleBot( config.getCell( 'telegram_token' ) )
	print( "Successfully loginned in telegram!")
	input_telegram()

def input_telegram():

	if ( config.getCell('telegram_useProxy') ):
		proxyType = str( config.getCell('p_type') )
		proxyUserInfo = str( config.getCell('p_user') + ':' + config.getCell('p_password') )
		proxyData = str( config.getCell('p_host') + ':' + config.getCell('p_port') )
		telebot.apihelper.proxy = { 
		'http': '%s://%s@%s' % ( proxyType, proxyUserInfo, proxyData ),
		'https': '%s://%s@%s' % ( proxyType, proxyUserInfo, proxyData )
		}

	module.bot.set_update_listener( listener )
	while True: # Костыль на случай timeout'a
		try:
			module.bot.polling(none_stop=False)
		except:
			continue

#    ______               _       
#   |  ____|             | |      
#   | |____   _____ _ __ | |_ ___ 
#   |  __\ \ / / _ \ '_ \| __/ __|
#   | |___\ V /  __/ | | | |_\__ \
#   |______\_/ \___|_| |_|\__|___/
#
# Проверка на различные события

#Проверка на заявки в друзья
def checknewfriends():
	newfriends = module.vk.friends.getRequests( out=0, count=1, need_viewed=1 ) # Смотрим, если ли заявки в друзья
	if newfriends['count'] != 0:
		module.vk.friends.add( user_id= newfriends['items'] ) # Добавляем человека в друзья

#     _____ _   _      _                 
#    / ____| | (_)    | |                
#   | (___ | |_ _  ___| | _____ _ __ ___ 
#    \___ \| __| |/ __| |/ / _ \ '__/ __|
#    ____) | |_| | (__|   <  __/ |  \__ \
#   |_____/ \__|_|\___|_|\_\___|_|  |___/
#                                        
#                                        

# Загрузка стикеров в ВК
def addStickerIntoVK( path, sticker ):

	stickerList = []
	ourFile = path + sticker

	upload = vk_api.VkUpload( vk_session )
	photo = upload.photo( ourFile + ".png", album_id = config.getCell( 'vk_album_id' ) )

	if ( config.getCell('vk_detelestickers') ):
		os.remove( ourFile + ".png" )

	ourVK = 'photo{}_{}'.format( photo[0]['owner_id'], photo[0]['id'] )

	stickerList.append( { 'sticker_t':ourFile,
						  'sticker_vk':ourVK } )

	return stickerList

def saveSticker( stickerURL, attachment ):

	attachment = attachment.split('/')

	content = ur.urlopen( stickerURL ).read()

	path = attachment[0] + '/'
	if not os.path.exists( path ):
		os.makedirs( path )

	# Перекодирование из webp в png

	imageWebp = path + attachment[1]

	out = open( imageWebp, 'wb' )
	out.write( content )
	out.close()

	img = Image.open(imageWebp)

	if ( config.getCell('vk_sticker_EnableScale') ):
		scale = config.getCell( 'vk_sticker_size' )
		img.thumbnail((scale, scale))
	img.save( imageWebp + ".png", "PNG")
	os.remove( imageWebp )

	#print( 'Sticker saved!' )

	stickers = addStickerIntoVK( path, attachment[1] )
	db.addStickerIntoDb( stickers )

# Разработчикам на заметку:
# Telegram та ещё поехавшая вещь, иногда аттачменты идут с расширением файла, иногда - без него
# Из-за этого я долго не мог понять, почему одни стикеры отправляются нормально, а другие - выдают ошибку при отправке

#    ______ _             _      
#   |  ____(_)           | |     
#   | |__   _ _ __   __ _| | ___ 
#   |  __| | | '_ \ / _` | |/ _ \
#   | |    | | | | | (_| | |  __/
#   |_|    |_|_| |_|\__,_|_|\___|
#                                
# Пихаем функции в потоки

t1 = threading.Thread( target=init_vk )
t2 = threading.Thread( target=init_telegram )

t1.start()
t2.start()
t1.join()
t2.join()