Со времени описания технологии блокчейн в 2008-м году и появления первой реализации в 2009-м (биткоин) по настоящее время создано более тысячи криптовалют. Каждые несколько дней проводятся ICO. Многие занимаются майнингом или игрой на криптовалютных биржах.
В связи с тем, что валюты нестабильны и их курс постоянно меняется, во избежание потери сбережений важно иметь возможность в кратчайшие сроки получать актуальную информацию о курсе и состоянии своих счетов.
Так как информация о блокчейне общедоступна, то доступ к ней возможен с помощью веб-сервисов и мобильных приложений. Для мониторинга состояния счетов удобно использовать мультивалютные мобильные приложения. Однако из-за высокой скорости создания новых криптовалют не все разработчики успевают добавлять их поддержку, и пользователь вынужден устанавливать другие приложения с требуемой валютой, что сказывается на удобстве и на занимаемом объёме памяти устройства. Здесь нам на помощь приходит ещё один тренд современности – чат-боты, API управления которыми предоставляют большинство мессенджеров.
Рассмотрим создание чат-бота для Telegram, предоставляющего информацию о счетах такой криптовалюты, как Dogecoin. Dogecoin был представлен в 2013-м году и назван в честь интернет-мема Doge. Часто используется для сбора пожертвований и благотворительности.
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
// if "/setwallet" command
if (activity.Text == "/setwallet")
{
context.Call(new SetWalletDialog(), SetWalletDialogResumeAfter);
}
// if "/setwallet [address]" command
else if (activity.Text.Contains("/setwallet"))
{
var forvardedMsg = context.MakeMessage();
forvardedMsg.Text = activity.Text;
await context.Forward(new SetWalletDialog(), SetWalletDialogResumeAfter, forvardedMsg, CancellationToken.None);
}
// if "/balance [address]" command
else if (activity.Text.Contains("/balance"))
{
var forvardedMsg = context.MakeMessage();
forvardedMsg.Text = activity.Text;
await context.Forward(new GetBalanceDialog(), GetBalanceDialogResumeAfter, forvardedMsg, CancellationToken.None);
}
else
{
if (activity.Text == "/start") //start conversation
await GreetUser(context, result);
else if (activity.Text == "/help") //show help
await ShowHelp(context);
context.Wait(MessageReceivedAsync);
}
}
public async Task StartAsync(IDialogContext context)
{
var msg = context.Activity.AsMessageActivity();
if (msg.Text == "/setwallet")
await context.PostAsync("Enter DogeCoin wallet address, please!");
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
string address = "";
if ((message.Text != null) && (message.Text.Trim().Length > 0))
{
if (message.Text.Contains(" ")) //if "/setwallet [address]" command
{
address = message.Text.Replace("/setwallet ", "").Trim();
}
else
address = message.Text;
try
{
var balance = await Client.GetBalanceAsync(address);
if (balance.Success == 1)
{
context.UserData.SetValue("wallet", address);
context.Done(address);
}
else
await ProcessErrors(context);
}
catch(Exception ex)
{
await ProcessErrors(context);
}
}
}
private async Task ProcessErrors(IDialogContext context)
{
--attempts;
if (attempts > 0)
{
await context.PostAsync(ExceptionMessage);
context.Wait(MessageReceivedAsync);
}
else
{
/* Fails the current dialog, removes it from the dialog stack, and returns the exception to the
parent/calling dialog. */
context.Fail(new TooManyAttemptsException(ExceptionFinalMessage));
}
}
public static async Task<BalanceEntity> GetBalanceAsync(string address)
{
return await GetAsync<BalanceEntity>($"address/balance/{address}");
}
public class BalanceEntity
{
public string Balance { get; set; }
public int Success { get; set; }
}
public static async Task<T> GetAsync<T>(string path)
{
InitClient();
T entity = default(T);
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
var jsonString = await response.Content.ReadAsStringAsync();
entity = JsonConvert.DeserializeObject<T>(jsonString);
}
return entity;
}
public static void InitClient() => InitClient(WebApiHost);
public static void InitClient(string webApiHost)
{
WebApiHost = webApiHost;
if (client != null && !string.IsNullOrEmpty(WebApiHost) && client.BaseAddress.AbsoluteUri == WebApiHost)
return;
client = new HttpClient
{
BaseAddress = new Uri(WebApiHost)
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
if (activity.Text == "/start") //start conversation
await GreetUser(context, result);
else if (activity.Text == "/help") //show help
await ShowHelp(context);
context.Wait(MessageReceivedAsync);
private async Task GreetUser(IDialogContext context, IAwaitable<object> result)
{
await SendGreetMessage(context);
await ShowHelp(context);
}
private async Task SendGreetMessage(IDialogContext context)
{
var qrMsg = context.MakeMessage();
qrMsg.Text = "Welcome, Young Shibe!\r\n";
qrMsg.Attachments.Add(new Attachment()
{
ContentUrl = "http://www.stickpng.com/assets/images/5845e608fb0b0755fa99d7e7.png",
ContentType = "image/png",
Name = " "
});
await context.PostAsync(qrMsg);
}
public class ReceivedTransactionsResponse
{
public string Status { get; set; }
public ReceivedTransactionsEntity Data { get; set; }
}
public class ReceivedTransactionsEntity
{
public string Address { get; set; }
public List<ReceivedTransaction> Txs { get; set; }
}
public class ReceivedTransaction
{
public string Txid { get; set; }
public string Value { get; set; }
public string Confirmations { get; set; }
}
public static async Task<List<ReceivedTransaction>> GetReceivedTransactions(string address)
{
WebApiHost = WebApiHostChainSo;
var trs = await GetAsync<ReceivedTransactionsResponse>($"get_tx_received/DOGE/{address}");
WebApiHost = WebApiHostDogechain;
return trs?.Data?.Txs;
}
private async Task SendReport(IDialogContext context, string address, List<ReceivedTransaction> transactions)
{
Reporter repr = new Reporter();
if (context.Activity.ChannelId != "telegram")
return;
using (MemoryStream pdfReport = repr.GetReceivedTransactionsPdf(address, transactions))
{
TelegramBotClient client = new TelegramBotClient("telegram_bot_token");
var me = await client.GetMeAsync();
var chatId = context.Activity.From.Id;
await client.SendDocumentAsync(chatId, new FileToSend("Received transactions.pdf", pdfReport), "First 100 received transactions.");
}
}
public MemoryStream GetReceivedTransactionsPdf(string address, List<ReceivedTransaction> transactions, string afterTXID = null)
{
string appData = HostingEnvironment.MapPath("~/App_Data/");
Config.WebMode = true;
Report = new Report();
Report.Load(appData + "TransactionsReport.frx");
Report.RegisterData(transactions, "txs");
Report.GetDataSource("txs").Enabled = true;
(Report.FindObject("Data1") as DataBand).DataSource = Report.GetDataSource("txs");
Report.Prepare();
PDFExport pdf = new PDFExport();
MemoryStream exportStream = new MemoryStream();
Report.Export(pdf, exportStream);
exportStream.Position = 0;
return exportStream;
}
К сожалению, не доступен сервер mySQL