Во время конференции Microsoft Build 2016 был анонсирован Microsoft Bot Framework (сессия с Build 2016: видео). С его помощью можно создать бота (на C# или Node.js), которого потом можно подключить к различным каналам / приложениям: СМС, Skype, Telegram, Slack и т.д. Мы пишем бота, используя Bot Builder SDK от Microsoft, а все проблемы взаимодействия с третьесторонними API берет на себя Bot Connector (см. изображение). Звучит красиво, попробуем создать простого бота, который мог бы переводить деньги с карты на карту (логику перевода возьмем у Альфа Банка — тестовый стенд, описание API: Альфа Банк), испытав все прелести продукта, находящегося в альфа-версии.
Disclaimer: во время написания статьи Microsoft выпустил новую версию фреймворка, так что ждите вторую серию: мигрируем бота с v1 на V3.
[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<Message> Post([FromBody]Message message)
{
if (message.Type == "Message")
{
// calculate something for us to return
int length = (message.Text ?? string.Empty).Length;
// return our reply to the user
return message.CreateReplyMessage($"You sent {length} characters");
}
else
{
return HandleSystemMessage(message);
}
}
private Message HandleSystemMessage(Message message)
{
if (message.Type == "Ping")
{
Message reply = message.CreateReplyMessage();
reply.Type = "Ping";
return reply;
}
else if (message.Type == "DeleteUserData")
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == "BotAddedToConversation")
{
}
else if (message.Type == "BotRemovedFromConversation")
{
}
else if (message.Type == "UserAddedToConversation")
{
}
else if (message.Type == "UserRemovedFromConversation")
{
}
else if (message.Type == "EndOfConversation")
{
}
return null;
}
}
[Serializable]
public class CardToCardTransfer
{
[Prompt("Номер карты отправителя:")]
[Describe("Номер карты, с которой Вы хотите перевести деньги")]
public string SourceCardNumber;
[Prompt("Номер карты получателя:")]
[Describe("Номер карты, на которую Вы хотите перевести деньги")]
public string DestinationCardNumber;
[Prompt("VALID THRU (месяц):")]
[Describe("VALID THRU (месяц)")]
public Month ValidThruMonth;
[Prompt("VALID THRU (год):")]
[Describe("VALID THRU (год)")]
[Numeric(2016, 2050)]
public int ValidThruYear;
[Prompt("CVV:")]
[Describe("CVV (три цифры на обороте карточки)")]
public string CVV;
[Prompt("Сумма перевода (руб):")]
[Describe("Сумма перевода (руб)")]
public int Amount;
[Prompt("Комиссия (руб):")]
[Describe("Комиссия (руб)")]
public double Fee;
}
public static class CardToCardFormBuilder
{
public static IForm<CardToCardTransfer> MakeForm()
{
FormBuilder<CardToCardTransfer> _order = new FormBuilder<CardToCardTransfer>();
return _order
.Message("Добро пожаловать в сервис перевода денег с карты на карту!")
.Field(nameof(CardToCardTransfer.SourceCardNumber))
.Field(nameof(CardToCardTransfer.ValidThruMonth))
.Field(nameof(CardToCardTransfer.ValidThruYear))
.Field(nameof(CardToCardTransfer.DestinationCardNumber), null, validateCard)
.Field(nameof(CardToCardTransfer.CVV))
.Field(nameof(CardToCardTransfer.Amount))
.OnCompletionAsync(async (context, cardTocardTransfer) =>
{
Debug.WriteLine("{0}", cardTocardTransfer);
})
.Build();
}
}
public static class CardToCardFormBuilder
{
public static IForm<CardToCardTransfer> MakeForm()
{
FormBuilder<CardToCardTransfer> _order = new FormBuilder<CardToCardTransfer>();
ValidateAsyncDelegate<CardToCardTransfer> validateCard =
async (state, value) =>
{
var cardNumber = value as string;
string errorMessage;
ValidateResult result = new ValidateResult();
result.IsValid = CardValidator.IsCardValid(cardNumber, out errorMessage);
result.Feedback = errorMessage;
return result;
};
return _order
.Message("Добро пожаловать в сервис перевода денег с карты на карту!")
.Field(nameof(CardToCardTransfer.SourceCardNumber), null, validateCard)
.Field(nameof(CardToCardTransfer.Fee), state => false)
.Field(nameof(CardToCardTransfer.ValidThruMonth))
.Field(nameof(CardToCardTransfer.ValidThruYear))
.Field(nameof(CardToCardTransfer.DestinationCardNumber), null, validateCard)
.Field(nameof(CardToCardTransfer.CVV))
.Field(nameof(CardToCardTransfer.Amount), null,
async (state, value) =>
{
int amount = int.Parse(value.ToString());
var alfabankService = new AlfabankService();
string auth = await alfabankService.AuthorizePartner();
state.Fee = (double) await alfabankService.GetCommission(auth, state.SourceCardNumber, state.DestinationCardNumber, amount);
ValidateResult result = new ValidateResult();
result.IsValid = true;
return result;
})
.Confirm("Вы хотите перевести {Amount} рублей с карты {SourceCardNumber} на карту {DestinationCardNumber}? Комиссия составит {Fee} рублей. (y/n)")
.OnCompletionAsync(async (context, cardTocardTransfer) =>
{
Debug.WriteLine("{0}", cardTocardTransfer);
})
.Build();
}
}
[BotAuthentication]
public class MessagesController : ApiController
{
internal static IDialog<CardToCardTransfer> MakeRoot()
{
return Chain.From(() => FormDialog.FromForm(CardToCardFormBuilder.MakeForm))
.Do(async (context, order) =>
{
try
{
var completed = await order;
var alfaService = new AlfabankService();
string expDate = completed.ValidThruYear.ToString() + ((int)completed.ValidThruMonth).ToString("D2");
string confirmationUrl = await alfaService.TransferMoney(completed.SourceCardNumber, expDate, completed.CVV, completed.DestinationCardNumber, completed.Amount);
await context.PostAsync($"Осталось только подтвердить платеж. Перейдите по адресу {confirmationUrl}");
}
catch (FormCanceledException<CardToCardTransfer> e)
{
string reply;
if (e.InnerException == null)
{
reply = $"Вы прервали операцию, попробуем позже!";
}
else
{
reply = "Извините, произошла ошибка. Попробуйте позже.";
}
await context.PostAsync(reply);
}
});
}
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<Message> Post([FromBody]Message message)
{
if (message.Type == "Message")
{
return await Conversation.SendAsync(message, MakeRoot);
}
else
{
return HandleSystemMessage(message);
}
}
К сожалению, не доступен сервер mySQL