diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..cc8b97a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,206 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = true +dotnet_sort_system_directives_first = true + +# this. and Me. preferences +dotnet_style_qualification_for_event = true:warning +dotnet_style_qualification_for_field = true:warning +dotnet_style_qualification_for_method = true:warning +dotnet_style_qualification_for_property = true:warning + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = false:warning +dotnet_style_predefined_type_for_member_access = false:warning + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:warning +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:warning +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Expression-level preferences +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:warning +csharp_style_throw_expression = true:warning +dotnet_style_coalesce_expression = true:warning +dotnet_style_collection_initializer = true:warning +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_null_propagation = true:warning +dotnet_style_object_initializer = false:suggestion +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_prefer_compound_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_return = true:warning +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning + +# Field preferences +dotnet_style_readonly_field = true:warning + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:warning + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = true:silent +csharp_style_var_for_built_in_types = true:warning +csharp_style_var_when_type_is_apparent = true:warning + +# Expression-bodied members +csharp_style_expression_bodied_accessors = when_on_single_line:suggestion +csharp_style_expression_bodied_constructors = when_on_single_line:suggestion +csharp_style_expression_bodied_indexers = when_on_single_line:suggestion +csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion +csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion +csharp_style_expression_bodied_methods = when_on_single_line:suggestion +csharp_style_expression_bodied_operators = when_on_single_line:suggestion +csharp_style_expression_bodied_properties = when_on_single_line:suggestion + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:warning +csharp_style_pattern_matching_over_is_with_cast_check = true:warning + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:warning + +# Modifier preferences +csharp_prefer_static_local_function = false:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Code-block preferences +csharp_prefer_braces = true:warning +csharp_prefer_simple_using_statement = false:warning + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:warning +csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion + +# 'using' directive preferences +csharp_using_directive_placement = inside_namespace:warning + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +#### Diagnostic configuration #### + +dotnet_diagnostic.CA1028.severity = none +dotnet_diagnostic.CA1031.severity = none +dotnet_diagnostic.CA1303.severity = none +dotnet_diagnostic.CA1060.severity = none +dotnet_diagnostic.CA2101.severity = none + +dotnet_diagnostic.IDE0058.severity = none diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..1801de1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,19 @@ +**Version** + + +**Submitting a...** + +[ ] Bug report +[ ] Feature request + +**Current behavior:** + + +**Expected behavior:** + + +**Steps to reproduce:** + + +**Other information:** + diff --git a/.github/New Text Document.txt b/.github/New Text Document.txt new file mode 100644 index 0000000..a2a8551 --- /dev/null +++ b/.github/New Text Document.txt @@ -0,0 +1,39 @@ +## Description + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. + +Fixes # (issue) + +## Type of change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## How Has This Been Tested? [Optional] + +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration + +- [ ] Test A +- [ ] Test B + +**Test Configuration**: +* Firmware version: +* Hardware: +* Toolchain: +* SDK: + +## Checklist: + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules +- [ ] I have checked my code and corrected any misspellings \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..056734d --- /dev/null +++ b/.gitignore @@ -0,0 +1,311 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + + +# +# ANGULAR gitignore begin section +# + +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc + +# dependencies +/node_modules + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +testem.log +/typings + +# e2e +/e2e/*.js +/e2e/*.map + +# System Files +*.xcuserstate +*.xcactivitylog + +*.DotSettings diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..51879ab --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + 7.3 + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..eab197d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Anthony Steiner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/LoupedeckPackage.yaml b/LoupedeckPackage.yaml new file mode 100644 index 0000000..17e885a --- /dev/null +++ b/LoupedeckPackage.yaml @@ -0,0 +1,14 @@ +type: plugin4 +name: Webhooks +displayName: Webhooks +version: 1.0.0 +author: Steinerd.com +copyright: Copyright (c) 2022 Anthony Steiner + +supportedDevices: + - LoupedeckCt + - LoupedeckLive + +pluginFileName: WebhooksPlugin.dll +pluginFolderWin: ./bin/win/ +pluginFolderMac: ./bin/mac/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1da6e89 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# Webhooks Loupedeck Plugin +[![License](http://img.shields.io/:license-MIT-blue.svg?style=flat)](LICENSE) +![forks](https://img.shields.io/github/forks/Steinerd/Loupedeck.WebhooksPlugin?style=flat) +![stars](https://img.shields.io/github/stars/Steinerd/Loupedeck.WebhooksPlugin?style=flat) +![issues](https://img.shields.io/github/issues/Steinerd/Loupedeck.WebhooksPlugin?style=flat) +[![downloads](https://img.shields.io/github/downloads/Steinerd/Loupedeck.WebhooksPlugin/latest/total?style=flat)](https://github.com/Steinerd/Loupedeck.WebhooksPlugin/compare) + +There is seemingly no built-in way to trigger a "call-and-forget" webhook within the native Loupedeck application. + +The Webhooks Loupedeck Plugin corrects the obvious omission of fundamental functionality in a macro controller. + +## Credits + +This plugin makes heavy use of [HarSharp](https://github.com/giacomelli/HarSharp), and its dependancies. + +-------- + +# Table of Contents + +- [Installation](#installation) +- [Usage](#usage) +- [Support](#support) +- [Contributing](#contributing) +- [License](#license) + +# Installation + +
Loupedeck Installation + + + 1. Go to [latest release](https://github.com/Steinerd/Loupedeck.WebhooksPlugin/releases/latest), and download the `lplug4` file to you computer + 1. Open (normally double-click) to install, the Loupedeck software should take care of the rest + 1. Restart Loupedeck (if not handled by the installer) + 1. In the Loupedeck interface, enable **Webhooks** by clicking Manage plugins + 1. Check the Webhooks box on to enable + 1. Drag the desired control onto your layout + +Once click it will bring you to a dynamic playback device selection page. +
+ +
IDE Installation + Made with Visual Studio 2022, C# will likely only compile in VS2019 or greater. + + Assuming Loupedeck is already installed on your machine, make sure you've stopped it before you debug the project. + + Debugging _should_ build the solution, which will then output the DLL, config, and pdb into your `%LocalAppData%\Loupedeck\Plugins` directory. + + If all goes well, Loupedeck will then open and you can then debug. + +
+ +# Usage + +Follow the __Loupedeck Installation__ instructions above. + +You will need to familiarize yourself with HAR/Http Archive files. They're effectively just JSON files with specific schema for HTTP requests and their responses. + +There are numerous ways to create them, or export them in many different applications. Including Postman, Telerik Fiddler2, and even most browsers DevTools will allow you to export/copy web requests as HARs. + +An example HAR file for a fake IFTTT call can be found here: [example.har](example.har) + +***All HAR Files must be saved to `%userprofile%\.loupedeck\webhooks` *** + +You can have multiple `*.har` files with multiple requests, or one `.har` with all the requests. The plugin will treat them the same. + +Fields that deal in "size" or "times" can be set to 0. They're not used in the creation/execution of the requests. + +Once completed you will be able to add requests to the *Webhook actions* "Requsts" folder in the Loupedeck UI. + +1. Simply click the [+] button on the same row + 1. Skip the name it just gets clobbered the moment you select a hook +1. Select HTTP Method (you will only available ones from your HAR files) +1. Select Hook from the final dropdown and click Save + +The button generation leads a lot to be desired, I apologize. I personally just create a macro and drag it in as an action step. + +# Support + +[Submit an issue](https://github.com/Steinerd/Loupedeck.WebhooksPlugin/issues/new) + +Fill out the template to the best of your abilities and send it through. + +# Contribute + +Easily done. Just [open a pull request](https://github.com/Steinerd/Loupedeck.WebhooksPlugin/compare). + +Don't worry about specifics, I'll handle the minutia. + +# License +The MIT-License for this plugin can be reviewed at [LICENSE](LICENSE) attached to this repo. diff --git a/WebhooksPlugin.sln b/WebhooksPlugin.sln new file mode 100644 index 0000000..2eb5ffd --- /dev/null +++ b/WebhooksPlugin.sln @@ -0,0 +1,41 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32328.378 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebhooksPlugin", "WebhooksPlugin\WebhooksPlugin.csproj", "{6187A600-57A1-485D-A88E-54E639F498CA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{723E67AE-8DB8-4BD7-9234-6FA88C7A4AB3}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore + build-plugin.ps1 = build-plugin.ps1 + Directory.Build.props = Directory.Build.props + LICENSE = LICENSE + LoupedeckPackage.yaml = LoupedeckPackage.yaml + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{AD13D4C6-72ED-4D8E-B465-6ED7D2420F3C}" + ProjectSection(SolutionItems) = preProject + .github\ISSUE_TEMPLATE.md = .github\ISSUE_TEMPLATE.md + .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6187A600-57A1-485D-A88E-54E639F498CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6187A600-57A1-485D-A88E-54E639F498CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6187A600-57A1-485D-A88E-54E639F498CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6187A600-57A1-485D-A88E-54E639F498CA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {67C0F55D-B602-45A4-9E23-5F7A12159339} + EndGlobalSection +EndGlobal diff --git a/WebhooksPlugin/App.config b/WebhooksPlugin/App.config new file mode 100644 index 0000000..270f7d7 --- /dev/null +++ b/WebhooksPlugin/App.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WebhooksPlugin/Commands/WebhooksCommand.cs b/WebhooksPlugin/Commands/WebhooksCommand.cs new file mode 100644 index 0000000..20ab4d3 --- /dev/null +++ b/WebhooksPlugin/Commands/WebhooksCommand.cs @@ -0,0 +1,71 @@ +namespace Loupedeck.WebhooksPlugin +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Web; + + using Loupedeck.WebhooksPlugin.Extensions; + + public class WebhookCommand : PluginDynamicCommand + { + private static readonly Services.HarFileService harFileService = new Services.HarFileService(); + + public WebhookCommand() : base("Requests", "Webhook Requests", "Webhook Requests") + { + this.MakeProfileAction("tree"); + } + + protected override Boolean OnLoad() + { + return base.OnLoad(); + } + + protected override PluginProfileActionData GetProfileActionData() + { + var tree = new PluginProfileActionTree("Select HTTP Request to Run"); + + var webHooks = harFileService.Webhooks; + + var wehHooksByHosts = webHooks.GroupBy(gb => gb.Request.Method); + + tree.AddLevel("HTTP Method"); + tree.AddLevel("Hook"); + + foreach (var group in wehHooksByHosts) + { + var node = tree.Root.AddNode(group.Key); + + foreach (var entry in group) + { + node.AddItem(entry.Request.Comment ?? entry.Comment, entry.Request.Comment ?? entry.Comment, $"{entry.Request.Method} {entry.Request.Url}"); + } + } + + return tree; + } + + protected override String GetCommandDisplayName(String actionParameter, PluginImageSize imageSize) + { + return string.IsNullOrEmpty(actionParameter) ? "Requests" : actionParameter; + } + + protected override void RunCommand(String actionParameter) + { + harFileService.Webhooks.FirstOrDefault(f => f.Comment.Equals(actionParameter, StringComparison.OrdinalIgnoreCase))?.Request.Run(); + } + + protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) + { + using (var bitmapBuilder = new BitmapBuilder(imageSize)) + { + bitmapBuilder.Clear(BitmapColor.Black); + + bitmapBuilder.DrawText(actionParameter, BitmapColor.White, 15, 13, 3); + return bitmapBuilder.ToImage(); + } + } + } +} diff --git a/WebhooksPlugin/Extensions/HarExtensions.cs b/WebhooksPlugin/Extensions/HarExtensions.cs new file mode 100644 index 0000000..26025e7 --- /dev/null +++ b/WebhooksPlugin/Extensions/HarExtensions.cs @@ -0,0 +1,48 @@ +namespace Loupedeck.WebhooksPlugin.Extensions +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + using System.Net.Http; + + public static class HarExtensions + { + public static Task Run(this HarSharp.Request request) + { + return request.Method != HttpMethod.Get.Method + && ( + string.IsNullOrEmpty(request.PostData.MimeType) + || string.IsNullOrEmpty(request.Headers.FirstOrDefault(h => h.Name.ToLower() == "content-type")?.Value) + ) + ? Task.Factory.StartNew(() => { }) + : Task.Factory.StartNew(() => + { + using (var client = new HttpClient()) + using (var req = new HttpRequestMessage(new HttpMethod(request.Method), request.Url)) + { + request.Headers.ToList().ForEach(h => + { + req.Headers.TryAddWithoutValidation(h.Name, h.Value); + }); + + if (request.Method != HttpMethod.Get.Method) + { + req.Content = new StringContent(request.PostData.Text, System.Text.Encoding.UTF8, request.PostData.MimeType); + } + + using (var response = client.SendAsync(req).Result) + { + if ((int)response.StatusCode > 400) + { + Console.Error.WriteLine($"Webhoook threw an undesired status code of {(int)response.StatusCode} | Message: {response.Content.ReadAsStringAsync().Result}"); + return; + } + } + } + }); + } + } +} diff --git a/WebhooksPlugin/FodyWeavers.xml b/WebhooksPlugin/FodyWeavers.xml new file mode 100644 index 0000000..5029e70 --- /dev/null +++ b/WebhooksPlugin/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/WebhooksPlugin/FodyWeavers.xsd b/WebhooksPlugin/FodyWeavers.xsd new file mode 100644 index 0000000..44a5374 --- /dev/null +++ b/WebhooksPlugin/FodyWeavers.xsd @@ -0,0 +1,111 @@ + + + + + + + + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with line breaks. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with line breaks. + + + + + The order of preloaded assemblies, delimited with line breaks. + + + + + + This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. + + + + + Controls if .pdbs for reference assemblies are also embedded. + + + + + Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. + + + + + As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. + + + + + Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. + + + + + Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with |. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with |. + + + + + The order of preloaded assemblies, delimited with |. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/WebhooksPlugin/GlobalSuppressions.cs b/WebhooksPlugin/GlobalSuppressions.cs new file mode 100644 index 0000000..8844b09 --- /dev/null +++ b/WebhooksPlugin/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Style", "IDE0049:Use framework type", Justification = "", Scope = "module")] diff --git a/WebhooksPlugin/PluginConfiguration.json b/WebhooksPlugin/PluginConfiguration.json new file mode 100644 index 0000000..cda6f07 --- /dev/null +++ b/WebhooksPlugin/PluginConfiguration.json @@ -0,0 +1,4 @@ +{ + "displayName": "Webhooks Handler", + "supportedDevices": "Loupedeck20,Loupedeck30" +} diff --git a/WebhooksPlugin/Properties/AssemblyInfo.cs b/WebhooksPlugin/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d1e44c9 --- /dev/null +++ b/WebhooksPlugin/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Webhooks")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Steinerd.com")] +[assembly: AssemblyProduct("Loupedeck2")] +[assembly: AssemblyCopyright("Copyright (c) 2022 Anthony Steiner. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6187A600-57A1-485D-A88E-54E639F498CA")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("4.3.0.7478")] +[assembly: AssemblyVersion("1.0.*")] diff --git a/WebhooksPlugin/Resources/16.png b/WebhooksPlugin/Resources/16.png new file mode 100644 index 0000000..3c4d44d Binary files /dev/null and b/WebhooksPlugin/Resources/16.png differ diff --git a/WebhooksPlugin/Resources/256.png b/WebhooksPlugin/Resources/256.png new file mode 100644 index 0000000..fce4e20 Binary files /dev/null and b/WebhooksPlugin/Resources/256.png differ diff --git a/WebhooksPlugin/Resources/32.png b/WebhooksPlugin/Resources/32.png new file mode 100644 index 0000000..24b0467 Binary files /dev/null and b/WebhooksPlugin/Resources/32.png differ diff --git a/WebhooksPlugin/Resources/48.png b/WebhooksPlugin/Resources/48.png new file mode 100644 index 0000000..6a1d622 Binary files /dev/null and b/WebhooksPlugin/Resources/48.png differ diff --git a/WebhooksPlugin/Services/HarFileService.cs b/WebhooksPlugin/Services/HarFileService.cs new file mode 100644 index 0000000..eee66f2 --- /dev/null +++ b/WebhooksPlugin/Services/HarFileService.cs @@ -0,0 +1,99 @@ +namespace Loupedeck.WebhooksPlugin.Services +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + using HarSharp; + + public class HarFileService + { + + public List Webhooks { get; private set; } + + private static string FULL_PATH { get => Path.Combine(WebhooksPlugin.UserProfilePath, WebhooksPlugin.DEFAULT_PATH); } + + private FileSystemWatcher _fileWatcher = null; + + public HarFileService(params string[] paths) + { + if (this.Webhooks == null) + { + this.Webhooks = new List(); + } + + this.Init(paths); + + this._fileWatcher = new FileSystemWatcher(FULL_PATH); + + this._fileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.Size; + this._setFileWatchHandlers(); + this._fileWatcher.Filter = "*.har"; + this._fileWatcher.IncludeSubdirectories = true; + this._fileWatcher.EnableRaisingEvents = true; + } + private void _setFileWatchHandlers() + { + this._fileWatcher.Created += this.OnFileChange; + this._fileWatcher.Changed += this.OnFileChange; + this._fileWatcher.Deleted += this.OnFileChange; + } + + private void _unsetFileWatchHanders() + { + this._fileWatcher.Created -= this.OnFileChange; + this._fileWatcher.Changed -= this.OnFileChange; + this._fileWatcher.Deleted -= this.OnFileChange; + } + + + ~HarFileService() + { + if (this._fileWatcher != null) + { + this._unsetFileWatchHanders(); + this._fileWatcher.Dispose(); + this._fileWatcher = null; + } + } + + public void OnFileChange(object sender, FileSystemEventArgs e) + { + this._unsetFileWatchHanders(); + this.Webhooks.Clear(); + System.Threading.Tasks.Task.Delay(150).Wait(); + this.Webhooks.AddRange(this._loadFromPath(FULL_PATH)); + System.Threading.Tasks.Task.Delay(150).Wait(); + this._setFileWatchHandlers(); + } + + private void Init(string[] paths) + { + this.Webhooks.AddRange(this._loadFromPath(FULL_PATH)); + + if (paths.Length >= 0) + { + paths.ToList().ForEach(path => + { + this.Webhooks.AddRange(this._loadFromPath(path)); + }); + } + } + + private IList _loadFromPath(string path) + { + var entries = new List(); + var harFiles = System.IO.Directory.GetFiles(path, "*.har"); + + foreach (var file in harFiles) + { + entries.AddRange(HarConvert.DeserializeFromFile(file).Log.Entries); + } + + return entries; + } + } +} diff --git a/WebhooksPlugin/WebhooksApplication.cs b/WebhooksPlugin/WebhooksApplication.cs new file mode 100644 index 0000000..9cf55fb --- /dev/null +++ b/WebhooksPlugin/WebhooksApplication.cs @@ -0,0 +1,12 @@ +namespace Loupedeck.WebhooksPlugin +{ + using System; + + public class WebhooksApplication : ClientApplication + { + public WebhooksApplication() { } + + protected override String GetProcessName() => String.Empty; + protected override String GetBundleName() => String.Empty; + } +} \ No newline at end of file diff --git a/WebhooksPlugin/WebhooksPlugin.cs b/WebhooksPlugin/WebhooksPlugin.cs new file mode 100644 index 0000000..8380257 --- /dev/null +++ b/WebhooksPlugin/WebhooksPlugin.cs @@ -0,0 +1,36 @@ +namespace Loupedeck.WebhooksPlugin +{ + using System; + using System.IO; + + public class WebhooksPlugin : Plugin + { + internal const string DEFAULT_PATH = @".loupedeck\webhooks"; + internal static string UserProfilePath { get => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); } + + public override Boolean HasNoApplication => true; + public override Boolean UsesApplicationApiOnly => true; + + public override void Load() => this.Init(); + + public override void Unload() { } + + private void OnApplicationStarted(Object sender, EventArgs e) { } + + private void OnApplicationStopped(Object sender, EventArgs e) { } + + public override void RunCommand(String commandName, String parameter) { } + + public override void ApplyAdjustment(String adjustmentName, String parameter, Int32 diff) { } + + private void Init() + { + this.Info.Icon16x16 = EmbeddedResources.ReadImage("Loupedeck.WebhooksPlugin.Resources.16.png"); + this.Info.Icon32x32 = EmbeddedResources.ReadImage("Loupedeck.WebhooksPlugin.Resources.32.png"); + this.Info.Icon48x48 = EmbeddedResources.ReadImage("Loupedeck.WebhooksPlugin.Resources.48.png"); + this.Info.Icon256x256 = EmbeddedResources.ReadImage("Loupedeck.WebhooksPlugin.Resources.256.png"); + + Directory.CreateDirectory(Path.Combine(UserProfilePath, DEFAULT_PATH)); + } + } +} diff --git a/WebhooksPlugin/WebhooksPlugin.csproj b/WebhooksPlugin/WebhooksPlugin.csproj new file mode 100644 index 0000000..c258583 --- /dev/null +++ b/WebhooksPlugin/WebhooksPlugin.csproj @@ -0,0 +1,99 @@ + + + + + + Debug + AnyCPU + {6187A600-57A1-485D-A88E-54E639F498CA} + Library + Properties + Loupedeck.WebhooksPlugin + WebhooksPlugin + v4.7.2 + 512 + + + + + $(SolutionDir)..\obj\ + $(SolutionDir)..\bin\ + $(BaseOutputPath)$(Configuration)\ + + + true + full + false + ..\..\..\..\AppData\Local\Loupedeck\Plugins\Webhooks\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + TRACE + prompt + 4 + ..\..\..\..\AppData\Local\Loupedeck\Plugins\Webhooks\ + + + + ..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll + + + ..\packages\HarSharp.2.0.0\lib\netstandard2.0\HarSharp.dll + + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + False + C:\Program Files (x86)\Loupedeck\Loupedeck2\PluginApi.dll + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/WebhooksPlugin/packages.config b/WebhooksPlugin/packages.config new file mode 100644 index 0000000..23110b4 --- /dev/null +++ b/WebhooksPlugin/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/build-plugin.ps1 b/build-plugin.ps1 new file mode 100644 index 0000000..65b1b93 --- /dev/null +++ b/build-plugin.ps1 @@ -0,0 +1,26 @@ +$version = "1.0" +$project = "Webhooks" +$dllName = "WebhooksPlugin.dll" +$dllPath = "$($env:LOCALAPPDATA)\Loupedeck\Plugins\$project" +$buildPath = ".builds" +$outputFileName = "$project.$version" +$zipPath = "$buildPath\$outputFileName.zip" +$pluginName = "$outputFileName.lplug4" +$loupedeckYaml = "LoupedeckPackage.yaml" +$cwd = Get-Location + +New-Item -Path "$buildPath" -Force -Name "bin" -ItemType "directory" > $null +New-Item -Path "$buildPath\bin" -Force -Name "win" -ItemType "directory" > $null +New-Item -Path "$buildPath\bin" -Force -Name "mac" -ItemType "directory" > $null + +Copy-Item $loupedeckYaml -Force -Destination $buildPath > $null +Copy-Item "$dllPath\$dllName" -Force -Destination "$buildPath\bin\win\$dllName" > $null + +$compress = @{ + Path = "$buildPath\*" + CompressionLevel = "Fastest" + DestinationPath = $zipPath +} +Compress-Archive @Compress > $null + +Rename-Item $zipPath -Force -NewName $pluginName > $null \ No newline at end of file diff --git a/example.har b/example.har new file mode 100644 index 0000000..8ec4999 --- /dev/null +++ b/example.har @@ -0,0 +1,104 @@ +{ + "log": { + "pages": [], + "comment": "", + "entries": [ + { + "comment": "IFTTT - Example ON", + "time": 0, + "serverIPAddress": "108.156.91.103", + "connection": "ClientPort:0;EgressPort:62999", + "request": { + "headersSize": 0, + "postData": { + "text": "", + "mimeType": "application/json" + }, + "queryString": [], + "headers": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Accept", + "value": "application/json" + }, + { + "name": "Host", + "value": "maker.ifttt.com" + } + ], + "bodySize": 0, + "url": "https://maker.ifttt.com/trigger/random_trigger_on/with/key/bloop-blop", + "cookies": [], + "method": "GET", + "httpVersion": "HTTP/1.1" + }, + "timings": { + "blocked": -1, + "ssl": 0, + "receive": 0, + "wait": 0, + "dns": 0, + "send": 0, + "connect": 0 + }, + "response": { }, + "startedDateTime": "2022-04-08T16:33:37.2014031-04:00", + "cache": {} + }, + { + "comment": "IFTTT - Example OFF", + "time": 0, + "serverIPAddress": "108.156.91.103", + "connection": "ClientPort:0;EgressPort:62999", + "request": { + "headersSize": 0, + "postData": { + "text": "", + "mimeType": "application/json" + }, + "queryString": [], + "headers": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Accept", + "value": "application/json" + }, + { + "name": "Host", + "value": "maker.ifttt.com" + } + ], + "bodySize": 0, + "url": "https://maker.ifttt.com/trigger/random_trigger_off/with/key/bloop-blop", + "cookies": [], + "method": "GET", + "httpVersion": "HTTP/1.1" + }, + "timings": { + "blocked": -1, + "ssl": 0, + "receive": 0, + "wait": 0, + "dns": 0, + "send": 0, + "connect": 0 + }, + "response": {}, + "startedDateTime": "2022-04-08T16:33:37.2014031-04:00", + "cache": {} + } + ], + "creator": { + "name": "Fiddler", + "comment": "https://fiddler2.com", + "version": "5.0.20211.51073" + }, + "version": "1.2" + } +} \ No newline at end of file