Compare commits

...
Author SHA1 Message Date
benbierens aa58a7aee4 add instructions link QR 2024-11-09 02:54:48 +01:00
benbierens 7b45a1171e Ads QR codes for CIDs 2024-11-08 16:09:47 +01:00
benbierens b1cb61b51c link to devcon setup instructions 2024-11-01 10:46:34 +01:00
benbierens 768bfcc8eb working upload both local and cloud-9 node 2024-11-01 10:04:19 +01:00
benbierens 8af91ea74b working local 2024-11-01 09:51:20 +01:00
benbierens c785df3adf implementing 2024-11-01 09:24:46 +01:00
benbierens bb3f0fccd0 set up plan 2024-11-01 08:48:42 +01:00
10 changed files with 363 additions and 1 deletions
+9
View File
@@ -0,0 +1,9 @@
<Application x:Class="DevconBoothImages.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DevconBoothImages"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
+14
View File
@@ -0,0 +1,14 @@
using System.Configuration;
using System.Data;
using System.Windows;
namespace DevconBoothImages
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}
+10
View File
@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
+77
View File
@@ -0,0 +1,77 @@
using CodexOpenApi;
using IdentityModel.Client;
using System.Net.Http;
using System.Windows;
using Utils;
namespace DevconBoothImages
{
public class Codexes
{
public Codexes(CodexApi local, CodexApi testnet)
{
Local = local;
Testnet = testnet;
}
public CodexApi Local { get; }
public CodexApi Testnet { get; }
}
public class CodexWrapper
{
public async Task<Codexes> GetCodexes()
{
var config = new Configuration();
return new Codexes(
await GetCodexWithPort(config.CodexLocalEndpoint),
await GetCodexWithoutPort(config.CodexPublicEndpoint, config.AuthUser, config.AuthPw)
);
}
private async Task<CodexApi> GetCodexWithPort(string endpoint)
{
var splitIndex = endpoint.LastIndexOf(':');
var host = endpoint.Substring(0, splitIndex);
var port = Convert.ToInt32(endpoint.Substring(splitIndex + 1));
var address = new Address(
host: host,
port: port
);
var client = new HttpClient();
var codex = new CodexApi(client);
codex.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1";
await CheckCodex(codex, endpoint);
return codex;
}
private async Task<CodexApi> GetCodexWithoutPort(string endpoint, string user, string pw)
{
var client = new HttpClient();
client.SetBasicAuthentication(user, pw);
var codex = new CodexApi(client);
codex.BaseUrl = $"{endpoint}/api/codex/v1";
await CheckCodex(codex, endpoint);
return codex;
}
private async Task CheckCodex(CodexApi codex, string endpoint)
{
try
{
var info = await codex.GetDebugInfoAsync();
if (string.IsNullOrEmpty(info.Id)) throw new Exception("Failed to fetch Codex node id");
}
catch (Exception ex)
{
MessageBox.Show($"Failed to connect to codex '{endpoint}': {ex}");
throw;
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
namespace DevconBoothImages
{
public class Configuration
{
public string CodexLocalEndpoint { get; } = "http://localhost:8080";
public string CodexPublicEndpoint { get; } = "https://api.testnet.codex.storage/storage/node-9";
public string AuthUser { get; } = "";
public string AuthPw { get; } = "";
public string LocalNodeBootstrapInfo { get; } = "";
public string WorkingDir { get; } = "D:\\DevconBoothApp";
}
}
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QRCoder" Version="1.6.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ProjectPlugins\CodexPlugin\CodexPlugin.csproj" />
<ProjectReference Include="..\Tools\AutoClient\AutoClient.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<ItemGroup>
<ApplicationDefinition Update="App.xaml">
<SubType>Designer</SubType>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<Page Update="MainWindow.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
</Project>
+38
View File
@@ -0,0 +1,38 @@
<Window x:Class="DevconBoothImages.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DevconBoothImages"
mc:Ignorable="d"
Title="CodexBoothImages" Height="450" Width="800" WindowState="Maximized">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Name="Img" />
<StackPanel Grid.Column="1">
<TextBlock Text="Instructions:"/>
<Image Name="ImgInstructions" />
<TextBlock Text="Local CID:"/>
<Image Name="ImgLocalCid" />
<TextBlock Text="TestNet CID:"/>
<Image Name="ImgTestnetCid" />
</StackPanel>
</Grid>
<StackPanel Grid.Row="1">
<TextBlock Name="Txt" HorizontalAlignment="Center" />
<Button Content="Check Codex connections" Click="Button_Click_2" />
<Button Content="Generate image -> Upload to Codex -> Put CID info in clipboard" Padding="10" Click="Button_Click"/>
<Button Content="Put last CID info in clipboard" Padding="10" Click="Button_Click_1"/>
</StackPanel>
</Grid>
</Window>
+160
View File
@@ -0,0 +1,160 @@
using AutoClient;
using CodexOpenApi;
using Logging;
using QRCoder;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace DevconBoothImages
{
public partial class MainWindow : Window
{
private readonly Configuration config = new Configuration();
private readonly CodexWrapper codexWrapper = new CodexWrapper();
private readonly ImageGenerator imageGenerator = new ImageGenerator(new NullLog());
private string currentLocalCid = string.Empty;
private string currentPublicCid = string.Empty;
public MainWindow()
{
InitializeComponent();
Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
}
private void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show("Unhandled exception: " + e.Exception);
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
// image
Log("Getting image...");
var file = await imageGenerator.Generate();
var filename = Path.Combine(config.WorkingDir, file);
File.Copy(file, filename);
var bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri(filename);
bmp.EndInit();
Img.Source = bmp;
Log("Uploading...");
// upload
await UploadToCodexes(filename, file);
// clipboard info
InfoToClipboard();
}
private BitmapImage GenerateQr(string text)
{
using (QRCodeGenerator qrGenerator = new QRCodeGenerator())
using (QRCodeData qrCodeData = qrGenerator.CreateQrCode(text, QRCodeGenerator.ECCLevel.Default))
using (PngByteQRCode qrCode = new PngByteQRCode(qrCodeData))
{
byte[] qrCodeImage = qrCode.GetGraphic(7);
using (var ms = new MemoryStream(qrCodeImage))
{
var img = Image.FromStream(ms);
using (var ms2 = new MemoryStream())
{
img.Save(ms2, ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = ms2;
bitmapImage.EndInit();
return bitmapImage;
}
}
}
}
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
InfoToClipboard();
}
private async void Button_Click_2(object sender, RoutedEventArgs e)
{
// check codexes
Log("Checking Codex connections...");
await codexWrapper.GetCodexes();
Log("Connections OK");
}
private async Task UploadToCodexes(string filename, string shortName)
{
var codexes = await codexWrapper.GetCodexes();
try
{
currentLocalCid = await UploadFile(filename, shortName, codexes.Local);
currentPublicCid = await UploadFile(filename, shortName, codexes.Testnet);
}
catch (Exception ex)
{
MessageBox.Show("Upload failed: " + ex);
}
Log($"Generated CIDs");
}
private async Task<string> UploadFile(string filename, string shortName, CodexApi codex)
{
using (var fileStream = File.OpenRead(filename))
{
var response = await codex.UploadAsync(
"image/jpeg",
$"attachment; filename=\"{shortName}\"",
fileStream);
if (string.IsNullOrEmpty(response) ||
response.ToLowerInvariant().Contains("unable to store block"))
{
throw new Exception("Unable to upload image. Response empty or error message.");
}
return response;
}
}
private void InfoToClipboard()
{
Clipboard.Clear();
if (string.IsNullOrEmpty(currentLocalCid) || string.IsNullOrEmpty(currentPublicCid))
{
Log("No CIDs were generated! Clipboard cleared.");
return;
}
var nl = Environment.NewLine;
var msg =
$"** Codex@Devcon 💻 Raspberry Pi Challenge **{nl}" +
$"📢 A new image is available. Download it and bring it to the booth!{nl}" +
$"Public Testnet CID: `{currentPublicCid}`{nl}" +
$"Local Devcon network CID: `{currentLocalCid}`{nl}" +
$"Setup instructions: [Here](https://docs.codex.storage){nl}" +
$"Local Devcon network information: [Here](https://github.com/codex-storage/codex-testnet-starter/blob/master/SETUP_DEVCONNET.md)";
Clipboard.SetText(msg);
Log("CID info copied to clipboard. Paste it in Discord plz!");
ImgLocalCid.Source = GenerateQr(currentLocalCid);
ImgTestnetCid.Source = GenerateQr(currentPublicCid);
ImgInstructions.Source = GenerateQr("https://github.com/codex-storage/codex-testnet-starter/blob/master/SETUP_DEVCONNET.md");
}
private void Log(string v)
{
Txt.Text = v;
}
}
}
+8 -1
View File
@@ -76,7 +76,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TranscriptAnalysis", "Tools
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MarketInsights", "Tools\MarketInsights\MarketInsights.csproj", "{004614DF-1C65-45E3-882D-59AE44282573}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsvCombiner", "Tools\CsvCombiner\CsvCombiner.csproj", "{6230347F-5045-4E25-8E7A-13D7221B7444}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CsvCombiner", "Tools\CsvCombiner\CsvCombiner.csproj", "{6230347F-5045-4E25-8E7A-13D7221B7444}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevconBoothImages", "DevconBoothImages\DevconBoothImages.csproj", "{92AC64E7-F6B1-474E-B915-30C8EEE2F9D7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -208,6 +210,10 @@ Global
{6230347F-5045-4E25-8E7A-13D7221B7444}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6230347F-5045-4E25-8E7A-13D7221B7444}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6230347F-5045-4E25-8E7A-13D7221B7444}.Release|Any CPU.Build.0 = Release|Any CPU
{92AC64E7-F6B1-474E-B915-30C8EEE2F9D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92AC64E7-F6B1-474E-B915-30C8EEE2F9D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92AC64E7-F6B1-474E-B915-30C8EEE2F9D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92AC64E7-F6B1-474E-B915-30C8EEE2F9D7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -244,6 +250,7 @@ Global
{C0EEBD32-23CB-45EC-A863-79FB948508C8} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3}
{004614DF-1C65-45E3-882D-59AE44282573} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3}
{6230347F-5045-4E25-8E7A-13D7221B7444} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3}
{92AC64E7-F6B1-474E-B915-30C8EEE2F9D7} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {237BF0AA-9EC4-4659-AD9A-65DEB974250C}