This commit is contained in:
2025-09-05 19:56:32 -07:00
commit 326d0808b8
82 changed files with 3482 additions and 0 deletions

296
.editorconfig Normal file
View File

@ -0,0 +1,296 @@
[*.md]
end_of_line = crlf
file_header_template = unset
indent_size = 2
indent_style = space
insert_final_newline = false
root = true
tab_width = 2
[*.csproj]
end_of_line = crlf
file_header_template = unset
indent_size = 2
indent_style = space
insert_final_newline = false
root = true
tab_width = 2
[*.cs]
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
csharp_new_line_before_catch = false
csharp_new_line_before_else = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = none
csharp_new_line_between_query_expression_clauses = true
csharp_prefer_braces = false
csharp_prefer_qualified_reference = true:error
csharp_prefer_simple_default_expression = true:warning
csharp_prefer_simple_using_statement = true:warning
csharp_prefer_static_local_function = true:warning
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
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
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
csharp_style_allow_embedded_statements_on_same_line_experimental = true
csharp_style_conditional_delegate_call = true
csharp_style_deconstructed_variable_declaration = false
csharp_style_expression_bodied_accessors = when_on_single_line:warning
csharp_style_expression_bodied_constructors = when_on_single_line:warning
csharp_style_expression_bodied_indexers = when_on_single_line:warning
csharp_style_expression_bodied_lambdas = when_on_single_line:warning
csharp_style_expression_bodied_local_functions = when_on_single_line:warning
csharp_style_expression_bodied_methods = when_on_single_line:warning
csharp_style_expression_bodied_operators = when_on_single_line:warning
csharp_style_expression_bodied_properties = when_on_single_line:warning
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
csharp_style_inlined_variable_declaration = false
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_pattern_local_over_anonymous_function = true:warning
csharp_style_pattern_matching_over_as_with_null_check = true:warning
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
csharp_style_prefer_index_operator = true:warning
csharp_style_prefer_not_pattern = true:warning
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_pattern_matching = true:warning
csharp_style_prefer_range_operator = true:warning
csharp_style_prefer_switch_expression = true:warning
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable:warning
csharp_style_unused_value_expression_statement_preference = discard_variable:warning
csharp_style_var_elsewhere = false:warning
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_using_directive_placement = outside_namespace
dotnet_analyzer_diagnostic.category-Design.severity = error
dotnet_analyzer_diagnostic.category-Documentation.severity = error
dotnet_analyzer_diagnostic.category-Globalization.severity = none
dotnet_analyzer_diagnostic.category-Interoperability.severity = error
dotnet_analyzer_diagnostic.category-Maintainability.severity = error
dotnet_analyzer_diagnostic.category-Naming.severity = none
dotnet_analyzer_diagnostic.category-Performance.severity = none
dotnet_analyzer_diagnostic.category-Reliability.severity = error
dotnet_analyzer_diagnostic.category-Security.severity = error
dotnet_analyzer_diagnostic.category-SingleFile.severity = error
dotnet_analyzer_diagnostic.category-Style.severity = error
dotnet_analyzer_diagnostic.category-Usage.severity = error
dotnet_code_quality_unused_parameters = all
dotnet_code_quality_unused_parameters = non_public
dotnet_code_quality.CAXXXX.api_surface = private, internal
dotnet_diagnostic.CA1001.severity = error # CA1001: Types that own disposable fields should be disposable
dotnet_diagnostic.CA1051.severity = error # CA1051: Do not declare visible instance fields
dotnet_diagnostic.CA1511.severity = warning # CA1511: Use 'ArgumentException.ThrowIfNullOrEmpty' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1513.severity = warning # Use 'ObjectDisposedException.ThrowIf' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1825.severity = warning # CA1825: Avoid zero-length array allocations
dotnet_diagnostic.CA1829.severity = warning # CA1829: Use Length/Count property instead of Count() when available
dotnet_diagnostic.CA1834.severity = warning # CA1834: Consider using 'StringBuilder.Append(char)' when applicable
dotnet_diagnostic.CA1860.severity = error # CA1860: Prefer comparing 'Count' to 0 rather than using 'Any()', both for clarity and for performance
dotnet_diagnostic.CA1862.severity = warning # CA1862: Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'
dotnet_diagnostic.CA1869.severity = none # CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead.
dotnet_diagnostic.CA2201.severity = none # CA2201: Exception type System.NullReferenceException is reserved by the runtime
dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template should not vary between calls to 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'
dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name
dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2");
dotnet_diagnostic.IDE0004.severity = warning # IDE0004: Cast is redundant.
dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary
dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement (IDE0010)
dotnet_diagnostic.IDE0028.severity = error # IDE0028: Collection initialization can be simplified
dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031)
dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed
dotnet_diagnostic.IDE0048.severity = none # Parentheses preferences (IDE0047 and IDE0048)
dotnet_diagnostic.IDE0049.severity = warning # Use language keywords instead of framework type names for type references (IDE0049)
dotnet_diagnostic.IDE0051.severity = error # Private member '' is unused [, ]
dotnet_diagnostic.IDE0058.severity = warning # IDE0058: Expression value is never used
dotnet_diagnostic.IDE0060.severity = error # IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0074.severity = warning # IDE0074: Use compound assignment
dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure (IDE0130)
dotnet_diagnostic.IDE0270.severity = warning # IDE0270: Null check can be simplified
dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]csharp(IDE0290)
dotnet_diagnostic.IDE0300.severity = error # IDE0300: Collection initialization can be simplified
dotnet_diagnostic.IDE0301.severity = error #IDE0301: Collection initialization can be simplified
dotnet_diagnostic.IDE0305.severity = none # IDE0305: Collection initialization can be simplified
dotnet_naming_rule.abstract_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.abstract_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.abstract_method_should_be_pascal_case.symbols = abstract_method
dotnet_naming_rule.class_should_be_pascal_case.severity = warning
dotnet_naming_rule.class_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.class_should_be_pascal_case.symbols = class
dotnet_naming_rule.delegate_should_be_pascal_case.severity = warning
dotnet_naming_rule.delegate_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.delegate_should_be_pascal_case.symbols = delegate
dotnet_naming_rule.enum_should_be_pascal_case.severity = warning
dotnet_naming_rule.enum_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.enum_should_be_pascal_case.symbols = enum
dotnet_naming_rule.event_should_be_pascal_case.severity = warning
dotnet_naming_rule.event_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.event_should_be_pascal_case.symbols = event
dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.method_should_be_pascal_case.severity = warning
dotnet_naming_rule.method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.method_should_be_pascal_case.symbols = method
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.private_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.private_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.private_method_should_be_pascal_case.symbols = private_method
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.severity = warning
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.style = private_of_internal_field
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.severity = warning
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.style = private_of_internal_field
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.symbols = private_or_internal_static_field
dotnet_naming_rule.property_should_be_pascal_case.severity = warning
dotnet_naming_rule.property_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.property_should_be_pascal_case.symbols = property
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.severity = warning
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.style = private_of_internal_field
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.symbols = public_or_protected_field
dotnet_naming_rule.static_field_should_be_pascal_case.severity = warning
dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field
dotnet_naming_rule.static_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.static_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.static_method_should_be_pascal_case.symbols = static_method
dotnet_naming_rule.struct_should_be_pascal_case.severity = warning
dotnet_naming_rule.struct_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.struct_should_be_pascal_case.symbols = struct
dotnet_naming_rule.types_should_be_pascal_case.severity = warning
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_style.begins_with_i.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.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.private_of_internal_field.capitalization = pascal_case
dotnet_naming_style.private_of_internal_field.required_prefix = _
dotnet_naming_style.private_of_internal_field.required_suffix =
dotnet_naming_style.private_of_internal_field.word_separator =
dotnet_naming_symbols.abstract_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.abstract_method.applicable_kinds = method
dotnet_naming_symbols.abstract_method.required_modifiers = abstract
dotnet_naming_symbols.class.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.class.applicable_kinds = class
dotnet_naming_symbols.class.required_modifiers =
dotnet_naming_symbols.delegate.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.delegate.applicable_kinds = delegate
dotnet_naming_symbols.delegate.required_modifiers =
dotnet_naming_symbols.enum.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.enum.applicable_kinds = enum
dotnet_naming_symbols.enum.required_modifiers =
dotnet_naming_symbols.event.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.event.applicable_kinds = event
dotnet_naming_symbols.event.required_modifiers =
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.method.applicable_accessibilities = public
dotnet_naming_symbols.method.applicable_kinds = method
dotnet_naming_symbols.method.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.private_method.applicable_accessibilities = private
dotnet_naming_symbols.private_method.applicable_kinds = method
dotnet_naming_symbols.private_method.required_modifiers =
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static
dotnet_naming_symbols.property.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.property.applicable_kinds = property
dotnet_naming_symbols.property.required_modifiers =
dotnet_naming_symbols.public_or_protected_field.applicable_accessibilities = public, protected
dotnet_naming_symbols.public_or_protected_field.applicable_kinds = field
dotnet_naming_symbols.public_or_protected_field.required_modifiers =
dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.static_field.applicable_kinds = field
dotnet_naming_symbols.static_field.required_modifiers = static
dotnet_naming_symbols.static_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.static_method.applicable_kinds = method
dotnet_naming_symbols.static_method.required_modifiers = static
dotnet_naming_symbols.struct.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.struct.applicable_kinds = struct
dotnet_naming_symbols.struct.required_modifiers =
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.required_modifiers =
dotnet_remove_unnecessary_suppression_exclusions = 0
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
dotnet_style_allow_multiple_blank_lines_experimental = false:warning
dotnet_style_allow_statement_immediately_after_block_experimental = true
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true:warning
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true:warning
dotnet_style_object_initializer = true:warning
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true:warning
dotnet_style_prefer_auto_properties = true:warning
dotnet_style_prefer_compound_assignment = true:warning
dotnet_style_prefer_conditional_expression_over_assignment = false
dotnet_style_prefer_conditional_expression_over_return = false
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
dotnet_style_prefer_inferred_tuple_names = true:warning
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
dotnet_style_prefer_simplified_boolean_expressions = true:warning
dotnet_style_prefer_simplified_interpolation = true
dotnet_style_qualification_for_event = false:error
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false:error
dotnet_style_qualification_for_property = false:error
dotnet_style_readonly_field = true:warning
dotnet_style_require_accessibility_modifiers = for_non_interface_members
end_of_line = crlf
file_header_template = unset
indent_size = 4
indent_style = space
insert_final_newline = false
root = true
tab_width = 4
# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822
# https://github.com/dotnet/aspnetcore/blob/main/.editorconfig
# https://github.com/dotnet/project-system/blob/main/.editorconfig

337
.gitignore vendored Normal file
View File

@ -0,0 +1,337 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# 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/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# 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
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.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
# Visual Studio Trace Files
*.e2e
# 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
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# 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
# Note: 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
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable 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
*.appx
# 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
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# 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
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# 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
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
.extensions-vscode
.extensions-vscode-oss
.extensions-vscode-insiders
.vscode/.UserSecrets/secrets.json
.vscode/jsonl/*

25
.vscode/bash.md vendored Normal file
View File

@ -0,0 +1,25 @@
# Bash
```Powershell 1731637312952 = 638672341129520000 = Thu Nov 14 2024 19:21:52 GMT-0700 (Mountain Standard Time)
dotnet sln add (ls -r **/**.csproj)
```
```bash 1731641980552 = 638672387805520000 = Thu Nov 14 2024 20:39:40 GMT-0700 (Mountain Standard Time)
dotnet new global.json --roll-forward latestMinor --sdk-version 8.0.100
```
```bash 1731642143081 = 638672389430810000 = Thu Nov 14 2024 20:42:22 GMT-0700 (Mountain Standard Time)
dotnet run --project src/Band
```
```bash 1731643960696 = 638672407606960000 = Thu Nov 14 2024 21:12:40 GMT-0700 (Mountain Standard Time)
docker compose up --build
```
```bash 1732418534573 = 638680153345730000 = Sat Nov 23 2024 20:22:14 GMT-0700 (Mountain Standard Time)
# docker system prune
docker build .
docker login gitea.phares.duckdns.org:443
docker tag fa136e5bb221 gitea.phares.duckdns.org:443/phares3757/band:latest
docker push gitea.phares.duckdns.org:443/phares3757/band:latest
```

1
.vscode/format-report.json vendored Normal file
View File

@ -0,0 +1 @@
[]

51
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,51 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "Build",
"program": "${workspaceFolder}/src/Band/bin/Debug/net9.0/Band.dll",
"args": [
"s",
"test"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
},
{
"type": "node",
"request": "launch",
"name": "node Launch Current Opened File",
"program": "${file}"
},
{
"name": "Docker",
"type": "coreclr",
"request": "attach",
"sourceFileMap": {
"/app": "${workspaceRoot}"
},
"processId": "${command:pickRemoteProcess}",
"pipeTransport": {
"debuggerPath": "/root/vsdbg/vsdbg",
"pipeProgram": "docker",
"pipeCwd": "${workspaceRoot}",
"quoteArgs": false,
"pipeArgs": [
"exec -i band-server"
]
}
},
]
}

5
.vscode/mklink.md vendored Normal file
View File

@ -0,0 +1,5 @@
# mklink
```bash 1731634239041 = 638672310390410000 = Thu Nov 14 2024 18:30:38 GMT-0700 (Mountain Standard Time)
mklink /J "L:\Git\band\.vscode\.UserSecrets" "C:\Users\phares\AppData\Roaming\Microsoft\UserSecrets\3c442d62-4cb9-45d5-9d09-3bae4a998ada"
```

5
.vscode/readme.md vendored Normal file
View File

@ -0,0 +1,5 @@
# Readme
1. Controllers => Presentation logic
1. Services => application logic
1. Domain => domain logic (business)

25
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,25 @@
{
"files.associations": {
"*.ffs_gui": "xml",
"*.hurl": "http",
"*.org": "ini",
"*.net": "ini",
"default": "ini"
},
"[markdown]": {
"editor.wordWrap": "off"
},
"files.exclude": {
"**/.git": false,
"**/node_modules": true
},
"files.watcherExclude": {
"**/node_modules": true
},
"cSpell.words": [
"accessibilities",
"aspnet",
"ASPNETCORE",
"CAXXXX"
]
}

187
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,187 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "User Secrets Init",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/src/Band/Band.csproj",
"init"
],
"problemMatcher": "$msCompile"
},
{
"label": "User Secrets Set",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/src/Band/Band.csproj",
"set",
"_UserSecretsId",
"3c442d62-4cb9-45d5-9d09-3bae4a998ada"
],
"problemMatcher": "$msCompile"
},
{
"label": "Format",
"command": "dotnet",
"type": "process",
"args": [
"format",
"--report",
".vscode",
"--verbosity",
"detailed",
"--severity",
"warn"
],
"problemMatcher": "$msCompile"
},
{
"label": "Format Whitespaces",
"command": "dotnet",
"type": "process",
"args": [
"format",
"whitespace"
],
"problemMatcher": "$msCompile"
},
{
"label": "Build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/src/Band/Band.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/src/Band/Band.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/src/Band/Band.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "Podman Login",
"command": "podman",
"type": "process",
"args": [
"login",
"gitea.phares.duckdns.org:443"
],
"problemMatcher": "$msCompile"
},
{
"label": "Podman Compose Up Build",
"command": "podman",
"type": "process",
"args": [
"compose",
"up",
"--build"
],
"problemMatcher": "$msCompile"
},
{
"label": "Podman Build",
"command": "podman",
"type": "process",
"args": [
"build",
"-t",
"band",
"."
],
"problemMatcher": "$msCompile"
},
{
"label": "Podman Image List",
"command": "podman",
"type": "process",
"args": [
"image",
"ls"
],
"problemMatcher": "$msCompile"
},
{
"label": "Podman Run",
"command": "podman",
"type": "process",
"args": [
"run",
"-p",
"5001:5001",
"--name",
"band_webapp",
"a3de856b5731"
],
"problemMatcher": "$msCompile"
},
{
"label": "Podman Tag",
"command": "podman",
"type": "process",
"args": [
"tag",
"asdf",
"gitea.phares.duckdns.org:443/phares3757/band:latest"
],
"problemMatcher": "$msCompile"
},
{
"label": "Podman Push",
"command": "podman",
"type": "process",
"args": [
"push",
"gitea.phares.duckdns.org:443/phares3757/band:latest"
],
"problemMatcher": "$msCompile"
},
{
"label": "Publish AOT",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"-r",
"win-x64",
"-c",
"Release",
"-p:PublishAot=true",
"${workspaceFolder}/src/Band/Band.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

27
Band.sln Normal file
View File

@ -0,0 +1,27 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{726EA01C-9356-430F-B1AE-7F22BF7387E4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Band", "src\Band\Band.csproj", "{1CE5AD06-2C61-45DA-A07F-981CF40C2485}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1CE5AD06-2C61-45DA-A07F-981CF40C2485}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1CE5AD06-2C61-45DA-A07F-981CF40C2485}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1CE5AD06-2C61-45DA-A07F-981CF40C2485}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1CE5AD06-2C61-45DA-A07F-981CF40C2485}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{1CE5AD06-2C61-45DA-A07F-981CF40C2485} = {726EA01C-9356-430F-B1AE-7F22BF7387E4}
EndGlobalSection
EndGlobal

25
Dockerfile Normal file
View File

@ -0,0 +1,25 @@
# Stage 1: Build Stage
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
# restore
COPY ["src/Band/Band.csproj", "Band/"]
RUN dotnet restore 'Band/Band.csproj'
# build
COPY ["src/Band", "Band/"]
WORKDIR /src/Band
RUN dotnet build 'Band.csproj' -c Release -o /app/build
# Stage 2: Publish Stage
FROM build AS publish
RUN dotnet publish 'Band.csproj' -c Release -o /app/publish
# Stage 3: Run Stage
FROM mcr.microsoft.com/dotnet/aspnet:9.0
ENV ASPNETCORE_HTTP_PORTS=5001
EXPOSE 5001
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT [ "dotnet", "Band.dll" ]

10
docker-compose.yaml Normal file
View File

@ -0,0 +1,10 @@
services:
webapp:
container_name: band
build:
context: .
dockerfile: Dockerfile
ports:
- "5006:5001"
environment:
- ASPNETCORE_ENVIRONMENT=Production

6
global.json Normal file
View File

@ -0,0 +1,6 @@
{
"sdk": {
"rollForward": "latestMinor",
"version": "9.0.100"
}
}

113
requests/Band.hurl Normal file
View File

@ -0,0 +1,113 @@
# https://auth.band.us/oauth2/authorize?response_type=code&client_id=465368400&redirect_uri=https%3A%2F%2Fband.phares.duckdns.org%2Fapi%2Fv1%2Fband%2Fdisplay
# GET http://127.0.0.1:5001/api/v1/band/display?code=
# ZQAAAYtb-dxVxHIHnZHMEQwRE1JqfysZTiKabqEyTIydwnSA-V_DJ2OqsBJEqsqq_01nuWBk3BUHOyzsp7QVU-rfU5N_GVcw1SZXRwSwAaXkNji5
# Accept: application/json
# HTTP 200
# [Asserts]
# header "Content-Type" == "application/json; charset=utf-8"
# jsonpath "$.result_code" == 1
###
GET http://127.0.0.1:5001/api/v1/band/bands
Accept: application/json
HTTP 200
[Asserts]
header "Content-Type" == "application/json; charset=utf-8"
jsonpath "$.result_code" == 1
jsonpath "$.result_data.bands" count > 0
###
GET http://127.0.0.1:5001/api/v1/band/AAAMyZx70eggR_WmoPaxRi0w/profile
Accept: application/json
HTTP 200
[Asserts]
header "Content-Type" == "application/json; charset=utf-8"
jsonpath "$.result_code" == 1
###
GET http://127.0.0.1:5001/api/v1/band/AAAMyZx70eggR_WmoPaxRi0w/posts
Accept: application/json
HTTP 200
[Asserts]
header "Content-Type" == "application/json; charset=utf-8"
jsonpath "$.result_code" == 1
###
GET http://127.0.0.1:5001/api/v1/band/AAAMyZx70eggR_WmoPaxRi0w/post_key_bla/get-post
Accept: application/json
HTTP 200
[Asserts]
header "Content-Type" == "application/json; charset=utf-8"
jsonpath "$.result_code" == 211
###
GET http://127.0.0.1:5001/api/v1/band/AAAMyZx70eggR_WmoPaxRi0w/post_key_bla/comments
Accept: application/json
HTTP 200
[Asserts]
header "Content-Type" == "application/json; charset=utf-8"
jsonpath "$.result_code" == 211
###
GET http://127.0.0.1:5001/api/v1/band/AAAMyZx70eggR_WmoPaxRi0w/albums
Accept: application/json
HTTP 200
[Asserts]
header "Content-Type" == "application/json; charset=utf-8"
jsonpath "$.result_code" == 1
###
GET http://127.0.0.1:5001/api/v1/band/AAAMyZx70eggR_WmoPaxRi0w/photos?photo_album_key=photo_album_key_bla
Accept: application/json
HTTP 200
[Asserts]
header "Content-Type" == "application/json; charset=utf-8"
jsonpath "$.result_code" == 211
###
GET http://127.0.0.1:5001/api/v1/band/AAAMyZx70eggR_WmoPaxRi0w/permissions?permissions=permissions_bla
Accept: application/json
HTTP 200
[Asserts]
header "Content-Type" == "application/json; charset=utf-8"
jsonpath "$.result_code" == 1
###
# GET http://127.0.0.1:5001/api/v1/band/AAAMyZx70eggR_WmoPaxRi0w/post
# Accept: application/json
# HTTP 200
# [Asserts]
# header "Content-Type" == "application/json; charset=utf-8"
# jsonpath "$.result_code" == 1
###
# GET http://127.0.0.1:5001/api/v1/band/AAAMyZx70eggR_WmoPaxRi0w/post_key_bla/post-comment
# Accept: application/json
# HTTP 200
# [Asserts]
# header "Content-Type" == "application/json; charset=utf-8"
# jsonpath "$.result_code" == 1
###

13
src/Band/Band.csproj Normal file
View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TargetFramework>net9.0</TargetFramework>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<UserSecretsId>3c442d62-4cb9-45d5-9d09-3bae4a998ada</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="System.Text.Json" Version="9.0.7" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,88 @@
using Band.Models;
using Band.Services;
using Microsoft.AspNetCore.Mvc;
namespace Band.Controllers;
[ApiController]
[Route("api/v1/[controller]")]
public class BandController(BandService pushPostService, Dictionary<string, Token> tokens) : ControllerBase {
private readonly BandService _BandService = pushPostService;
private readonly Dictionary<string, Token> _Tokens = tokens;
[HttpGet("display")]
public IActionResult Get(string code) {
Token? token = _BandService.GetToken(code);
if (token is null) {
if (_Tokens is not null) {
_ = _BandService.GetAuthorizationCode();
}
return BadRequest("Unable to get token");
}
_Tokens.Add(code, token);
return Content(code, "text/plain");
}
[HttpGet("bands")]
public IActionResult Bands() {
Models.Bands.Root? result = _BandService.GetBands(_Tokens);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to get bands!") : Ok(result);
}
[HttpGet("{band_key}/profile")]
public IActionResult Profile(string band_key) {
Models.Profile.Root? result = _BandService.GetProfile(_Tokens, band_key);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to get profile!") : Ok(result);
}
[HttpGet("{band_key}/posts")]
public IActionResult Posts(string band_key) {
Models.Posts.Root? result = _BandService.GetPosts(_Tokens, band_key);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to get posts!") : Ok(result);
}
[HttpGet("{band_key}/{post_key}/get-post")]
public IActionResult GetPost(string band_key, string post_key) {
Models.Post.Root? result = _BandService.GetPost(_Tokens, band_key, post_key);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to get post!") : Ok(result);
}
[HttpGet("{band_key}/{post_key}/comments")]
public IActionResult Comments(string band_key, string post_key, string sort = "recent") {
Models.Comments.Root? result = _BandService.GetComments(_Tokens, band_key, post_key, sort);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to get comments!") : Ok(result);
}
[HttpGet("{band_key}/albums")]
public IActionResult Albums(string band_key) {
Models.Albums.Root? result = _BandService.GetAlbums(_Tokens, band_key);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to get albums!") : Ok(result);
}
[HttpGet("{band_key}/photos")]
public IActionResult Photos(string band_key, string photo_album_key) {
Models.Photos.Root? result = _BandService.GetPhotos(_Tokens, band_key, photo_album_key);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to get photos!") : Ok(result);
}
[HttpGet("{band_key}/permissions")]
public IActionResult Permissions(string band_key, string permissions) {
Models.Permissions.Root? result = _BandService.GetPermissions(_Tokens, band_key, permissions);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to get permissions!") : Ok(result);
}
[HttpGet("{band_key}/post")]
public IActionResult Post(string band_key, string message) {
Root? result = _BandService.WritePost(_Tokens, band_key, message);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to post message!") : Ok(result);
}
[HttpGet("{band_key}/post-comment")]
public IActionResult WritePostComment(string band_key, string post_key, string message) {
Root? result = _BandService.WritePostComment(_Tokens, band_key, post_key, message);
return result is null ? _Tokens.Count == 0 ? BadRequest("Tokens is empty!") : BadRequest("Unable to post message!") : Ok(result);
}
}

View File

@ -0,0 +1,22 @@
using Band.Models;
using Band.Services;
namespace Band.DependencyInjection;
public static class ServiceCollectionExtensions {
public static IServiceCollection AddServices(this IServiceCollection services, AppSettings appSettings, Dictionary<string, Token> tokens) {
_ = services.AddControllers();
_ = services.AddScoped<BandService>();
_ = services.AddSingleton(_ => tokens);
_ = services.AddSingleton(_ => appSettings);
if (appSettings.Settings.Band is not null && !string.IsNullOrEmpty(appSettings.Settings.Band.BasicAuthorization)) {
_ = services.AddHttpClient(nameof(AppSettings.Settings.Band), client => {
client.BaseAddress = new Uri("https://openapi.band.us/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
}
return services;
}
}

View File

@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Band.Models.Albums;
public record Item(
[property: JsonPropertyName("created_at")] object CreatedAt,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("owner")] Owner Owner,
[property: JsonPropertyName("photo_album_key")] string PhotoAlbumKey,
[property: JsonPropertyName("photo_count")] int PhotoCount
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Item))]
public partial class AlbumsItemSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Albums;
public record NextParams(
[property: JsonPropertyName("after")] string After,
[property: JsonPropertyName("band_key")] string BandKey
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(NextParams))]
public partial class AlbumsNextParamsSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Albums;
public record Paging(
[property: JsonPropertyName("next_params")] NextParams NextParams,
[property: JsonPropertyName("previous_params")] object PreviousParams
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Paging))]
public partial class AlbumsPagingSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace Band.Models.Albums;
public record Owner(
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("profile_image_url")] string ProfileImageUrl
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Owner))]
public partial class AlbumsOwnerSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace Band.Models.Albums;
public record ResultData(
[property: JsonPropertyName("items")] IReadOnlyList<Item> Items,
[property: JsonPropertyName("paging")] Paging Paging,
[property: JsonPropertyName("total_photo_count")] int TotalPhotoCount
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ResultData))]
public partial class AlbumsResultDataSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Albums;
public record Root(
[property: JsonPropertyName("result_code")] int ResultCode,
[property: JsonPropertyName("result_data")] ResultData ResultData
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
public partial class AlbumsRootSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,62 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Band.Models;
public record AppSettings(string Company, Settings Settings, string URLs, string[] WithOrigins) {
public override string ToString() {
string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
return result;
}
private static void Verify(AppSettings appSettings) {
if (appSettings?.Company is null) {
throw new NullReferenceException(nameof(Company));
}
if (appSettings?.URLs is null) {
throw new NullReferenceException(nameof(URLs));
}
if (appSettings?.WithOrigins is null) {
throw new NullReferenceException(nameof(WithOrigins));
}
if (appSettings?.Settings?.Band is null) {
throw new NullReferenceException(nameof(Settings.Band));
}
}
public static AppSettings Get(IConfigurationRoot configurationRoot) {
AppSettings result;
#pragma warning disable IL3050, IL2026
string? urls = configurationRoot.GetSection(nameof(URLs)).Get<string>();
string? company = configurationRoot.GetSection(nameof(Company)).Get<string>();
Settings? settings = configurationRoot.GetSection(nameof(Settings)).Get<Settings>();
string[]? withOrigins = configurationRoot.GetSection(nameof(WithOrigins)).Get<string[]>();
#pragma warning restore IL3050, IL2026
if (company is null || settings is null || urls is null || withOrigins is null) {
List<string> paths = [];
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) {
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) {
continue;
}
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) {
continue;
}
paths.Add(physicalFileProvider.Root);
}
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
}
result = new(Company: company,
Settings: settings,
URLs: urls,
WithOrigins: withOrigins);
Verify(result);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext {
}

21
src/Band/Models/Band.cs Normal file
View File

@ -0,0 +1,21 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Band.Models;
public record Band(string? BasicAuthorization,
int? ClientId,
string? Redirect,
string? Token) {
public override string ToString() {
string result = JsonSerializer.Serialize(this, BandSourceGenerationContext.Default.Band);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Band))]
public partial class BandSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace Band.Models.Bands;
public record Band(
[property: JsonPropertyName("band_key")] string BandKey,
[property: JsonPropertyName("cover")] string Cover,
[property: JsonPropertyName("member_count")] int MemberCount,
[property: JsonPropertyName("name")] string Name
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Band))]
public partial class BandsBandSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Band.Models.Bands;
public record ResultData(
[property: JsonPropertyName("bands")] IReadOnlyList<Band> Bands
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ResultData))]
public partial class BandsResultDataSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Bands;
public record Root(
[property: JsonPropertyName("result_code")] int ResultCode,
[property: JsonPropertyName("result_data")] ResultData ResultData
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
public partial class BandsRootSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace Band.Models.Comments;
public record Author(
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("profile_image_url")] string ProfileImageUrl
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Author))]
public partial class CommentsAuthorSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,19 @@
using System.Text.Json.Serialization;
namespace Band.Models.Comments;
public record Item(
[property: JsonPropertyName("author")] Author Author,
[property: JsonPropertyName("comments_key")] string CommentsKey,
[property: JsonPropertyName("content")] string Content,
[property: JsonPropertyName("created_at")] object CreatedAt,
[property: JsonPropertyName("emotion_count")] int EmotionCount,
[property: JsonPropertyName("is_audio_comments")] bool IsAudioComments,
[property: JsonPropertyName("photo")] Photo Photo,
[property: JsonPropertyName("sticker")] object Sticker
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Item))]
public partial class CommentsItemSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace Band.Models.Comments;
public record NextParams(
[property: JsonPropertyName("access_token")] string AccessToken,
[property: JsonPropertyName("after")] string After,
[property: JsonPropertyName("band_key")] string BandKey,
[property: JsonPropertyName("post_key")] string PostKey
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(NextParams))]
public partial class CommentsNextParamsSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Comments;
public record Paging(
[property: JsonPropertyName("next_params")] NextParams NextParams,
[property: JsonPropertyName("previous_params")] object PreviousParams
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Paging))]
public partial class CommentsPagingSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace Band.Models.Comments;
public record Photo(
[property: JsonPropertyName("height")] int Height,
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("width")] int Width
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Photo))]
public partial class CommentsPhotoSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace Band.Models.Comments;
public record ResultData(
[property: JsonPropertyName("items")] IReadOnlyList<Item> Items,
[property: JsonPropertyName("paging")] Paging Paging,
[property: JsonPropertyName("update_param")] UpdateParam UpdateParam
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ResultData))]
public partial class CommentsResultDataSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Comments;
public record Root(
[property: JsonPropertyName("result_code")] int ResultCode,
[property: JsonPropertyName("result_data")] ResultData ResultData
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
public partial class CommentsRootSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,11 @@
using System.Text.Json.Serialization;
namespace Band.Models.Comments;
public record UpdateParam(
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(UpdateParam))]
public partial class CommentsUpdateParamSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,99 @@
// public record Root(
// [property: JsonPropertyName("access_token")] string AccessToken,
// [property: JsonPropertyName("expires_in")] int ExpiresIn,
// [property: JsonPropertyName("refresh_token")] string RefreshToken,
// [property: JsonPropertyName("scope")] string Scope,
// [property: JsonPropertyName("token_type")] string TokenType,
// [property: JsonPropertyName("user_key")] string UserKey
// );
// https://developers.band.us/develop/guide/api/get_authorization_code_from_user
// https://auth.band.us/oauth2/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri urlencoded}
// Request
// GET /oauth2/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri urlencoded} HTTP/1.1
// Host: auth.band.us
// A user authorization process takes place within a popup window offered by BAND. The authorization code is sent to redirect_uri which has been passed as a parameter after a successful authorization.
// Response
// HTTP/1.1 302 Found
// Location: {redirect_uri}?code={authorization_code}
// Content-Length: 0
// Content-Type: text/plain; charset=UTF-8
// Request
// GET /oauth2/token?grant_type=authorization_code&code={authorization_code} HTTP/1.1
// Host: auth.band.us
// Authorization: Basic {base64 encoded '{client_id:client_secret}'}
// Response
// HTTP/1.1 200 OK
// Content-Type: application/json;charset=UTF-8
// {
// "access_token": "{access_token}",
// "expires_in": 315359999,
// "refresh_token": "{refresh_token}",
// "scope": "READ_PHOTO READ_ALBUM DELETE_POST READ_BAND_AND_USERS_LIST READ_BAND_PERMISSION CREATE_COMMENT READ_POST WRITE_POST READ_MY_PROFILE READ_COMMENT",
// "token_type": "bearer",
// "user_key": "{user_key}"
// }
// Reference: Interface Specification
// 1) Getting an authorization code
// Request
// URL
// [GET] https://auth.band.us/oauth2/authorize
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// response_type|string|Y|Must input code to get a proper response.
// client_id|Integer|Y|Client ID for your app. Issued when registering your service/app.
// redirect_uri|string|Y|Redirect path you wish to receive your authorization code.
// Response
// redirect uri
// {redirect_uri} received as a request parameter
// redirect parameter
// Parameters
// Name|Type|Description
// code|string|Authorization code issued
// 2) Issuing an access token
// Request
// URL
// [GET] https://auth.band.us/oauth2/token
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// code|string|Y|The authorization code passed
// grant_type|string|Y|Must input authorization_code to get a proper response.
// Header
// Header
// Name|Type|Mandatory|Description
// Authorization|string|Y|Use basic authentication scheme. Basic {perform base64 encoding after combining client_id and client_secret with a colon (:) in between}
// Response
// Content-Type
// : application/json;charset=UTF-8
// Response body
// Response body
// Name|Type|Description
// access_token|string|Access token
// user_key|string|User ID
// expires_in|Integer|Valid period of an access tokens
// token_type|string|Access token type
// Error Code
// Error Code
// http status|Error Code|Description
// 401|invalid_client|Cannot identify the client (client_id error)
// 401|invalid_token|Token does not exist or has been expired
// 400|invalid_request|Undefined error
// 400|invalid_grant|Token cannot be obtained. (wrong authorization_code or login info)
// 400|redirect_uri_mismatch|redirect_uri doesnt match the info given in service/app registration
// 400|unsupported_grant_type|The value for the grant_type is invalid
// 400|unsupported_response_type|The value for the response_type is invalid
// 400|access_denied|User doesnt allow your service/app to access his/her data
// 403|insufficient_scope|Resource server response. Access token does not have the scope of the requested API
// https://auth.band.us/oauth2/authorize?response_type=code&client_id=465368400&redirect_uri=https%3A%2F%2Fband.phares.duckdns.org%2Fapi%2Fv1%2Fband%2Fdisplay

View File

@ -0,0 +1,40 @@
// using System.Text.Json.Serialization;
// namespace Band.Models.Band;
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );
// public record ResultData(
// [property: JsonPropertyName("bands")] IReadOnlyList<Band> Bands
// );
// public record Band(
// [property: JsonPropertyName("band_key")] string BandKey,
// [property: JsonPropertyName("cover")] string Cover,
// [property: JsonPropertyName("member_count")] int MemberCount,
// [property: JsonPropertyName("name")] string Name
// );
// https://developers.band.us/develop/guide/api/get_bands
// {
// "result_code": 1,
// "result_data": {
// "bands": [
// {
// "band_key": "AzIEz54gxWeSAB_nwygZ84",
// "cover": "http://img.band.us/111.jpg",
// "member_count": 100,
// "name": "Golf Club"
// },
// {
// "band_key": "AzIEz54gxWeSAB_nwygZ95",
// "cover": "http://img.band.us/222.jpg",
// "member_count": 32,
// "name": "Baseball team"
// }
// ]
// }
// }

View File

@ -0,0 +1,32 @@
// https://developers.band.us/develop/guide/api/write_comment
// Request
// URL
// [POST] https://openapi.band.us/v2/band/post/comment/create
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// access_token|string|Y|Access token of the user
// band_key|string|Y|Band ID
// post_key|string|Y|Post ID
// body|string|Y|Comment content
// Response
// response body (json)
// response body (json)
// Name|Type|Description
// result_code|integer|Result code (success: 1)
// result_data.message|string|Result message
// {
// "result_code": 1,
// "result_data": {
// "message": "success"
// }
// }
// public record ResultData(
// [property: JsonPropertyName("message")] string Message
// );
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );

View File

@ -0,0 +1,37 @@
// https://developers.band.us/develop/guide/api/get_post_permission
// Request
// URL
// [GET] https://openapi.band.us/v2/band/permissions
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// access_token|string|Y|Access token of the user
// band_key|string|Y|Band ID
// permissions|list|Y|Permission list to check. Use a comma (,) to separate permissions. (ex, permissions=posting,commenting)
// posting: Post write permission
// commenting: Comment write permission
// contents_deletion: Post/Comment delete permission (Does not check the person who published it.)
// Response
// response body (json)
// response body (json)
// Name|Type|Description
// result_code|integer|Result code (success: 1)
// result_data.permissions|list|List of filtered permissions
// {
// "result_code": 1,
// "result_data": {
// "permission": [
// "posting",
// "commenting"
// ]
// }
// }
// public record ResultData(
// [property: JsonPropertyName("permission")] IReadOnlyList<string> Permission
// );
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );

View File

@ -0,0 +1,47 @@
// using System.Text.Json.Serialization;
// namespace Band.Models.Profile;
// public record ResultData(
// [property: JsonPropertyName("is_app_member")] bool IsAppMember,
// [property: JsonPropertyName("message_allowed")] bool MessageAllowed,
// [property: JsonPropertyName("name")] string Name,
// [property: JsonPropertyName("profile_image_url")] string ProfileImageUrl,
// [property: JsonPropertyName("user_key")] string UserKey
// );
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );
// https://developers.band.us/develop/guide/api/get_user_information
// Request
// URL
// [GET] https://openapi.band.us/v2/profile
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// access_token|string|Y|Access token of the user
// band_key|string|N|Band ID to get the user's profile from a specific band.
// If not provided, the default profile of the user will be returned.
// Response
// response body (json)
// response body (json)
// Name|Type|Description
// result_code|integer|Result code (success: 1)
// result_data.user_key|string|User ID
// result_data.profile_image_url|string|URL of a profile image
// result_data.name|string|User name
// result_data.is_app_member|bool|Boolean value indicating whether the user account is connected to your app or not.
// result_data.message_allowed|bool|Boolean value indicating whether the user allowed to receive messages or not.
// {
// "result_code": 1,
// "result_data": {
// "is_app_member": true,
// "message_allowed": true,
// "name": "Jordan Lee",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/xxxx/test.jpg",
// "user_key": "1egy84j58_testkey"
// }
// }

View File

@ -0,0 +1,181 @@
// https://developers.band.us/develop/guide/api/get_posts
// Request
// URL
// [GET] https://openapi.band.us/v2/band/posts
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// access_token|string|Y|Access token of the user
// band_key|string|Y|Band ID
// locale|string|Y|Region and language (ex. en_US)
// Response
// response body (json)
// response body (json)
// Name|Type|Description
// result_code|integer|Result code (success: 1)
// result_data.paging|object|Paging parameters
// result_data.items|list|Post list
// result_data > paging
// result_data > paging
// Name|Type|Description
// previous_params|object|Parameter information to get the previous page (currently not available)
// next_params|object|Parameter information to get the next page
// important
// next_params
// To request the pagination, use the next_params value as a parameter, which is returned as paging. For example, if "next_params" : { "after": "aaa", "limit": "20", "band_key" : "bbb", "access_token": "xxx" } is returned in the next_params field, call https://openapi.band.us/v2/band/posts?after=aaa&limit=20&band_key=bbb&access_token=xxx&locale=en_US to get the next page.
// Note that previous_params is not currently supported.
// result_data > items[i]
// result_data > items[i]
// Name|Type|Description
// content|string|Body of a post. Note that HTML tags are escaped (if applicable).
// ex) tag : <band:refer user_key="abcde">user name</band:refer>
// author|object|Author information
// post_key|string|Post ID
// comment_count|int|Band ID
// created_at|long|Published date and time
// photos|list|Information on photos and videos. Videos are shown first.
// emotion_count|int|The number of emotions
// latest_comments|list|Content of the latest comments in the post
// band_key|string|Band ID
// result_data > items[i] > author
// result_data > items[i] > author
// Name|Type|Description
// name|string|Author name
// description|string|Description about the author
// role|string|Author's role
// profile_image_url|string|URL of an author's profile image
// user_key|string|User ID of an author
// result_data > items[i] > photos
// result_data > items[i] > photos
// Name|Type|Description
// height|int|Height of an image
// width|int|Width of an image
// created_at|long|Created date and time
// url|string|URL of an image
// author|object|Information on the user who uploaded the photo/video. Same format as post author.
// photo_album_key|string|Album ID
// photo_key|string|Photo ID
// comment_count|int|The number of comments
// emotion_count|int|The number of emotions
// is_video_thumbnail|boolean|Indicates whether the image is a video thumbnail or not.
// result_data > items[i] > latest_comments
// result_data > items[i] > latest_comments
// Name|Type|Description
// body|string|Comment content
// author|object|Information on the user who commented. Same format as post author.
// created_at|long|Created date and time
// {
// "result_code": 1,
// "result_data": {
// "items": [
// {
// "author": {
// "description": "This is a description",
// "name": "Charley Lee",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/zzzzzz/zzzzz.jpg"
// },
// "comment_count": 7,
// "content": "Cotents <band:refer user_no=\"\">Jordan</band:refer> <band:hashtag>#food</band:hashtag>",
// "created_at": 1444288237000,
// "photos": [
// {
// "author": {
// "description": "This is desc.",
// "name": "Sujan",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/yyyyyy/yyyyy.jpg"
// },
// "comment_count": 1,
// "created_at": 1444295780000,
// "emotion_count": 1,
// "height": 480,
// "is_video_thumbnail": true,
// "photo_album_key": null,
// "photo_key": "AAACTLxEYqMx09qji9nzAv1V",
// "url": "http://beta.coresos.phinf.naver.net/a/2g8728_c/28aUd015g6pesvlf89uq_ggxdbs.jpg",
// "width": 640
// },
// {
// "author": {
// "description": "This contents is so...",
// "name": "Eddy",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/xxxxxx/xxxxxx.jpg"
// },
// "comment_count": 1,
// "created_at": 1444288237000,
// "emotion_count": 1,
// "height": 720,
// "is_video_thumbnail": false,
// "photo_album_key": null,
// "photo_key": "AADtBkdo52blVIcH-4y_AotK",
// "url": "http://beta.coresos.phinf.naver.net/a/2g8701_9/ac2Ud01515ftew9215e27_ggxdbs.jpg",
// "width": 1280
// }
// ],
// "post_key": "AABhEEQG08e1Fc227z2fRCwv"
// }
// ],
// "paging": {
// "next_params": {
// "access_token": "accsstokenxxxx",
// "after": "AABsFASmeIbSAib8KSzihCBC",
// "band_key": "bandkeyxxxx",
// "limit": "20"
// },
// "previous_params": null
// }
// }
// }
// public record Author(
// [property: JsonPropertyName("description")] string Description,
// [property: JsonPropertyName("name")] string Name,
// [property: JsonPropertyName("profile_image_url")] string ProfileImageUrl
// );
// public record Item(
// [property: JsonPropertyName("author")] Author Author,
// [property: JsonPropertyName("comment_count")] int CommentCount,
// [property: JsonPropertyName("content")] string Content,
// [property: JsonPropertyName("created_at")] long CreatedAt,
// [property: JsonPropertyName("photos")] IReadOnlyList<Photo> Photos,
// [property: JsonPropertyName("post_key")] string PostKey
// );
// public record NextParams(
// [property: JsonPropertyName("access_token")] string AccessToken,
// [property: JsonPropertyName("after")] string After,
// [property: JsonPropertyName("band_key")] string BandKey,
// [property: JsonPropertyName("limit")] string Limit
// );
// public record Paging(
// [property: JsonPropertyName("next_params")] NextParams NextParams,
// [property: JsonPropertyName("previous_params")] object PreviousParams
// );
// public record Photo(
// [property: JsonPropertyName("author")] Author Author,
// [property: JsonPropertyName("comment_count")] int CommentCount,
// [property: JsonPropertyName("created_at")] object CreatedAt,
// [property: JsonPropertyName("emotion_count")] int EmotionCount,
// [property: JsonPropertyName("height")] int Height,
// [property: JsonPropertyName("is_video_thumbnail")] bool IsVideoThumbnail,
// [property: JsonPropertyName("photo_album_key")] object PhotoAlbumKey,
// [property: JsonPropertyName("photo_key")] string PhotoKey,
// [property: JsonPropertyName("url")] string Url,
// [property: JsonPropertyName("width")] int Width
// );
// public record ResultData(
// [property: JsonPropertyName("items")] IReadOnlyList<Item> Items,
// [property: JsonPropertyName("paging")] Paging Paging
// );
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );

View File

@ -0,0 +1,182 @@
// https://developers.band.us/develop/guide/api/get_post
// Request
// URL
// [GET] https://openapi.band.us/v2.1/band/post
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// access_token|string|Y|Access token of the user
// band_key|string|Y|Band ID
// post_key|string|Y|Post ID
// Response
// response body (json)
// response body (json)
// Name|Type|Description
// result_code|integer|Result code (success: 1)
// result_data.post|object|Post details
// result_data > post
// result_data > post
// Name|Type|Description
// content|string|Body of a post. Note that HTML tags are escaped (if applicable).
// ex) tag : <band:refer user_key="abcde">user name</band:refer>
// author|object|Author information
// post_key|string|Post ID
// comment_count|int|The number of comments
// created_at|long|Published date and time
// photo|object|Information on photos and videos. Videos are shown first.
// emotion_count|int|The number of emotions
// band_key|string|Band ID
// post_read_count|int|Read count
// result_data > post > author
// result_data > post > author
// Name|Type|Description
// name|string|Author name
// description|string|Description about the author
// role|string|Author's role
// profile_image_url|string|URL of an author's profile image
// user_key|string|User ID of an author
// result_data > post > photo
// result_data > post > photo
// Name|Type|Description
// height|int|Height of an image
// width|int|Width of an image
// created_at|long|Created date and time
// url|string|URL of an image
// author|object|Information on the user who uploaded the photo/video. Same format as post author.
// photo_album_key|string|Album ID
// photo_key|string|Photo ID
// comment_count|int|The number of comments
// emotion_count|int|The number of emotions
// is_video_thumbnail|boolean|Indicates whether the image is a video thumbnail or not.
// {
// "result_code": 1,
// "result_data": {
// "post": {
// "author": {
// "description": "",
// "name": "Jordan",
// "profile_image_url": "http://coresos.phinf.naver.net/a/yyy/xxx.png",
// "role": "member",
// "user_key": "AACv2efNJhMjHOgGWhfhXg25"
// },
// "band_key": "AAAbBbMeQHBLh3-y8xxogqBg",
// "comment_count": 0,
// "content": "This is Cotents.\n To be continue... <band:attachment type=\"photo\" id=\"3452\" /><band:attachment type=\"video\" id=\"675\" />",
// "created_at": 1484303791000,
// "emotion_count": 0,
// "is_multilingual": false,
// "photo": {
// "5110026620": {
// "author": {
// "description": "",
// "name": "Charley",
// "profile_image_url": "http://coresos.phinf.naver.net/a/2hj6eg_i/yyyyy.png",
// "role": "member",
// "user_key": "AACv2efNJhMjHOgGWhfhXg25"
// },
// "comment_count": 0,
// "created_at": 1484303791000,
// "emotion_count": 0,
// "height": 1920,
// "is_video_thumbnail": false,
// "photo_album_key": null,
// "photo_key": "AACVbWOG62mcbDJjaYOkSO2c",
// "url": "http://coresos.phinf.naver.net/a/2hjc4i_d/zzzzz.jpg",
// "width": 1440
// }
// },
// "post_key": "AAAFJEEalAmkhLXGh0rQCy3h",
// "post_read_count": 0,
// "shared_count": 0,
// "video": {
// "175284037": {
// "author": {
// "description": "",
// "name": "미니0",
// "profile_image_url": "http://coresos.phinf.naver.net/a/xxxx/xxxxx.png",
// "role": "member",
// "user_key": "AACv2efNJhMjHOgGWhfhXg25"
// },
// "comment_count": 0,
// "created_at": 1484303791000,
// "emotion_count": 0,
// "height": 360,
// "is_video_thumbnail": true,
// "photo_album_key": null,
// "photo_key": "AAD7LTDVWfB5GE80Usi_0Y_H",
// "url": "http://coresos.phinf.naver.net/a/yyyy/yyyy.jpg",
// "width": 640
// }
// }
// }
// }
// }
// public record _175284037(
// [property: JsonPropertyName("author")] Author Author,
// [property: JsonPropertyName("comment_count")] int CommentCount,
// [property: JsonPropertyName("created_at")] long CreatedAt,
// [property: JsonPropertyName("emotion_count")] int EmotionCount,
// [property: JsonPropertyName("height")] int Height,
// [property: JsonPropertyName("is_video_thumbnail")] bool IsVideoThumbnail,
// [property: JsonPropertyName("photo_album_key")] object PhotoAlbumKey,
// [property: JsonPropertyName("photo_key")] string PhotoKey,
// [property: JsonPropertyName("url")] string Url,
// [property: JsonPropertyName("width")] int Width
// );
// public record _5110026620(
// [property: JsonPropertyName("author")] Author Author,
// [property: JsonPropertyName("comment_count")] int CommentCount,
// [property: JsonPropertyName("created_at")] long CreatedAt,
// [property: JsonPropertyName("emotion_count")] int EmotionCount,
// [property: JsonPropertyName("height")] int Height,
// [property: JsonPropertyName("is_video_thumbnail")] bool IsVideoThumbnail,
// [property: JsonPropertyName("photo_album_key")] object PhotoAlbumKey,
// [property: JsonPropertyName("photo_key")] string PhotoKey,
// [property: JsonPropertyName("url")] string Url,
// [property: JsonPropertyName("width")] int Width
// );
// public record Author(
// [property: JsonPropertyName("description")] string Description,
// [property: JsonPropertyName("name")] string Name,
// [property: JsonPropertyName("profile_image_url")] string ProfileImageUrl,
// [property: JsonPropertyName("role")] string Role,
// [property: JsonPropertyName("user_key")] string UserKey
// );
// public record Photo(
// [property: JsonPropertyName("5110026620")] _5110026620 _5110026620
// );
// public record Post(
// [property: JsonPropertyName("author")] Author Author,
// [property: JsonPropertyName("band_key")] string BandKey,
// [property: JsonPropertyName("comment_count")] int CommentCount,
// [property: JsonPropertyName("content")] string Content,
// [property: JsonPropertyName("created_at")] long CreatedAt,
// [property: JsonPropertyName("emotion_count")] int EmotionCount,
// [property: JsonPropertyName("is_multilingual")] bool IsMultilingual,
// [property: JsonPropertyName("photo")] Photo Photo,
// [property: JsonPropertyName("post_key")] string PostKey,
// [property: JsonPropertyName("post_read_count")] int PostReadCount,
// [property: JsonPropertyName("shared_count")] int SharedCount,
// [property: JsonPropertyName("video")] Video Video
// );
// public record ResultData(
// [property: JsonPropertyName("post")] Post Post
// );
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );
// public record Video(
// [property: JsonPropertyName("175284037")] _175284037 _175284037
// );

View File

@ -0,0 +1,159 @@
// https://developers.band.us/develop/guide/api/get_comments
// This API lets you get comments of a specific post.
// [GET] https://openapi.band.us/v2/band/post/comments
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// access_token|string|Y|Access token of the user
// band_key|string|Y|Band ID
// post_key|string|Y|Post ID
// sort|string|N|Sorting types. The default order is sorted by created date.
// +created_at: Sorted by created date
// -created_at: Sorted by last modified date
// Response
// response body (json)
// response body (json)
// Name|Type|Description
// result_code|integer|Result code (success: 1)
// result_data.paging|object|Paging parameters
// result_data.items|list|Post list
// result_data > paging
// result_data > paging
// Name|Type|Description
// previous_params|object|Parameter information to get the previous page (currently not available)
// next_params|object|Parameter information to get the next page
// important
// next_params
// To request the pagination, use the next_params value as a parameter, which is returned as paging. For example, if "next_params" : { "after": "aaa", "limit": "20", "band_key" : "bbb", "access_token": "xxx" } is returned in the next_params field, call https://openapi.band.us/v2/band/posts?after=aaa&limit=20&band_key=bbb&access_token=xxx&locale=en_US to get the next page.
// Note that previous_params is not currently supported.
// result_data > items[i]
// result_data > items[i]
// Name|Type|Description
// band_key|string|Body of a post. Note that HTML tags are escaped (if applicable).
// ex) tag : <band:refer user_key="abcde">user name</band:refer>
// author|object|Band ID
// post_key|string|Post ID
// comment_key|string|Comment ID
// content|string|Comment content
// emotion_count|integer|The number of emotions
// is_audio_included|bool|Indicates whether the comment is audible or not.
// created_at|long|Comment date and time
// author|object|Author information
// photo|object|Photo information
// result_data > items[i] > author
// result_data > items[i] > author
// Name|Type|Description
// name|string|Author name
// description|string|Description about the author
// role|string|Author's role
// profile_image_url|string|URL of an author's profile image
// user_key|string|User ID of an author
// result_data > items[i] > photo
// result_data > items[i] > photo
// Name|Type|Description
// url|string|URL of an image
// height|int|Height of an image
// width|int|Width of an image
// Note
// Time is shown in epoch value.
// {
// "result_code": 1,
// "result_data": {
// "items": [
// {
// "author": {
// "description": "ㅋㅋ",
// "name": "Jordan Lee ",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/yyyy/xxxxx.jpg"
// },
// "comment_key": "AADGZXHAFeWdd1NaGsc5hN07",
// "content": "This is content... <band:refer user_no=\"122\">Sujan</band:refer>",
// "created_at": 1444110980000,
// "emotion_count": 1,
// "is_audio_comment": false,
// "photo": {
// "height": 720,
// "url": "http://beta.coresos.phinf.naver.net/a/2g85c8_c/173Ud015tfd2ude1umx7_ggxdbs.jpg",
// "width": 1280
// },
// "sticker": null
// },
// {
// "author": {
// "description": "I'm ... ",
// "name": "Jordan Lee ",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/xxx/yyyyyy.jpg"
// },
// "comment_key": "AABsFASmeIbSAib8KSzihCBC",
// "content": "This is Content...",
// "created_at": 1444199762000,
// "emotion_count": 0,
// "is_audio_comment": false,
// "photo": null,
// "sticker": null
// }
// ],
// "paging": {
// "next_params": {
// "access_token": "xxxx",
// "after": "AABsFASmeIbSAib8KSzihCBC",
// "band_key": "bxx",
// "post_key": "pxx"
// },
// "previous_params": null
// },
// "update_param": {}
// }
// }
// public record Author(
// [property: JsonPropertyName("description")] string Description,
// [property: JsonPropertyName("name")] string Name,
// [property: JsonPropertyName("profile_image_url")] string ProfileImageUrl
// );
// public record Item(
// [property: JsonPropertyName("author")] Author Author,
// [property: JsonPropertyName("comment_key")] string CommentKey,
// [property: JsonPropertyName("content")] string Content,
// [property: JsonPropertyName("created_at")] object CreatedAt,
// [property: JsonPropertyName("emotion_count")] int EmotionCount,
// [property: JsonPropertyName("is_audio_comment")] bool IsAudioComment,
// [property: JsonPropertyName("photo")] Photo Photo,
// [property: JsonPropertyName("sticker")] object Sticker
// );
// public record NextParams(
// [property: JsonPropertyName("access_token")] string AccessToken,
// [property: JsonPropertyName("after")] string After,
// [property: JsonPropertyName("band_key")] string BandKey,
// [property: JsonPropertyName("post_key")] string PostKey
// );
// public record Paging(
// [property: JsonPropertyName("next_params")] NextParams NextParams,
// [property: JsonPropertyName("previous_params")] object PreviousParams
// );
// public record Photo(
// [property: JsonPropertyName("height")] int Height,
// [property: JsonPropertyName("url")] string Url,
// [property: JsonPropertyName("width")] int Width
// );
// public record ResultData(
// [property: JsonPropertyName("items")] IReadOnlyList<Item> Items,
// [property: JsonPropertyName("paging")] Paging Paging,
// [property: JsonPropertyName("update_param")] UpdateParam UpdateParam
// );
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );
// public record UpdateParam(
// );

View File

@ -0,0 +1,116 @@
// https://developers.band.us/develop/guide/api/get_albums
// Request
// URL
// [GET] https://openapi.band.us/v2/band/albums
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// access_token|string|Y|Access token of the user
// band_key|string|Y|Band ID
// Response
// response body (json)
// response body (json)
// Name|Type|Description
// result_code|integer|Result code (success: 1)
// result_data.paging|object|Paging parameters
// result_data.items|list|Album list
// result_data > paging
// result_data > paging
// Name|Type|Description
// previous_params|object|Parameter information to get the previous page (currently not available)
// next_params|object|Parameter information to get the next page
// important
// next_params
// To request the pagination, use the next_params value as a parameter, which is returned as paging. For example, if "next_params" : { "after": "aaa", "limit": "20", "band_key" : "bbb", "access_token": "xxx" } is returned in the next_params field, call https://openapi.band.us/v2/band/posts?after=aaa&limit=20&band_key=bbb&access_token=xxx&locale=en_US to get the next page.
// Note that previous_params is not currently supported.
// result_data > items[i]
// result_data > items[i]
// Name|Type|Description
// photo_album_key|string|Album ID
// name|string|Album name
// photo_count|int|The number of photos in an album
// created_at|long|Created date and time
// author|object|Album creator information
// result_data > items[i] > author
// result_data > items[i] > author
// Name|Type|Description
// name|string|Author name
// description|string|Description about the author
// role|string|Author's role
// profile_image_url|string|URL of an author's profile image
// user_key|string|User ID of an author
// Note
// Time is shown in epoch value.
// {
// "result_code": 1,
// "result_data": {
// "items": [
// {
// "created_at": 1443778960000,
// "name": "Flower Album",
// "owner": {
// "description": "ㅋㅋ",
// "name": "Charley",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/xxx/test.jpg"
// },
// "photo_album_key": "AAA4EPU-k8RKboHPybmZaUw5",
// "photo_count": 1
// },
// {
// "created_at": 1443691414000,
// "name": "Europe Tour",
// "owner": {
// "description": "This is description.",
// "name": "Jordan Lee",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/xxxx/test.jpg"
// },
// "photo_album_key": "AABWu8gnqZaHTsKDa4zVn7hK",
// "photo_count": 3
// }
// ],
// "paging": {
// "next_params": {
// "after": "AABWu8gnqZaHTsKDa4zVn7hK",
// "band_key": "xxxx"
// },
// "previous_params": null
// },
// "total_photo_count": 10
// }
// }
// public record Item(
// [property: JsonPropertyName("created_at")] object CreatedAt,
// [property: JsonPropertyName("name")] string Name,
// [property: JsonPropertyName("owner")] Owner Owner,
// [property: JsonPropertyName("photo_album_key")] string PhotoAlbumKey,
// [property: JsonPropertyName("photo_count")] int PhotoCount
// );
// public record NextParams(
// [property: JsonPropertyName("after")] string After,
// [property: JsonPropertyName("band_key")] string BandKey
// );
// public record Owner(
// [property: JsonPropertyName("description")] string Description,
// [property: JsonPropertyName("name")] string Name,
// [property: JsonPropertyName("profile_image_url")] string ProfileImageUrl
// );
// public record Paging(
// [property: JsonPropertyName("next_params")] NextParams NextParams,
// [property: JsonPropertyName("previous_params")] object PreviousParams
// );
// public record ResultData(
// [property: JsonPropertyName("items")] IReadOnlyList<Item> Items,
// [property: JsonPropertyName("paging")] Paging Paging,
// [property: JsonPropertyName("total_photo_count")] int TotalPhotoCount
// );
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );

View File

@ -0,0 +1,129 @@
// https://developers.band.us/develop/guide/api/get_photos
// Request
// URL
// [GET] https://openapi.band.us/v2/band/album/photos
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// access_token|string|Y|Access token of the user
// band_key|string|Y|Band ID
// photo_album_key|string|N|Album ID
// Response
// response body (json)
// response body (json)
// Name|Type|Description
// result_code|integer|Result code (success: 1)
// result_data.paging|object|Paging parameters
// result_data.items|list|Image list
// result_data > paging
// result_data > paging
// Name|Type|Description
// previous_params|object|Parameter information to get the previous page (currently not available)
// next_params|object|Parameter information to get the next page
// result_data > items[i]
// result_data > items[i]
// Name|Type|Description
// photo_key|string|Photo ID
// url|string|URL of an image
// width|int|Width of an image
// height|int|Height of an image
// photo_album_key|string|Album ID
// created_at|long|Created date and time
// author|object|Author information
// comment_count|int|The number of comments
// emotion_count|int|The number of emotions
// result_data > items[i] > author
// result_data > items[i] > author
// Name|Type|Description
// name|string|Author name
// description|string|Description about the author
// role|string|Author's role
// profile_image_url|string|URL of an author's profile image
// user_key|string|User ID of an author
// {
// "result_code": 1,
// "result_data": {
// "items": [
// {
// "author": {
// "description": "This is description.",
// "name": "Jordan Lee",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/xxx/test.jpg"
// },
// "comment_count": 1,
// "created_at": 1443691385000,
// "emotion_count": 1,
// "height": 720,
// "is_video_thumbnail": false,
// "photo_album_key": "AADgiaZXYFi1lV-JpylzUvwO",
// "photo_key": "AACEdJ3oAh0ICHh98X5so5aI",
// "url": "http://beta.coresos.phinf.naver.net/a/xxxx/test.jpg",
// "width": 1280
// },
// {
// "author": {
// "description": "This is description.",
// "name": "Robert J. Lee",
// "profile_image_url": "http://band.phinf.campmobile.net/20130719_224/xxx/test.jpg"
// },
// "comment_count": 1,
// "created_at": 1443690955000,
// "emotion_count": 1,
// "height": 480,
// "is_video_thumbnail": true,
// "photo_album_key": "AADgiaZXYFi1lV-JpylzUvwO",
// "photo_key": "AAAVIQRg6e8ld2yf7eQZwWtf",
// "url": "http://beta.coresos.phinf.naver.net/a/xxxx/test.jpg",
// "width": 640
// }
// ],
// "paging": {
// "next_params": {
// "after": "294038803",
// "band_key": "dhjd8djd7",
// "photo_album_key": "wdfdf929"
// },
// "previous_params": null
// }
// }
// }
// public record Author(
// [property: JsonPropertyName("description")] string Description,
// [property: JsonPropertyName("name")] string Name,
// [property: JsonPropertyName("profile_image_url")] string ProfileImageUrl
// );
// public record Item(
// [property: JsonPropertyName("author")] Author Author,
// [property: JsonPropertyName("comment_count")] int CommentCount,
// [property: JsonPropertyName("created_at")] object CreatedAt,
// [property: JsonPropertyName("emotion_count")] int EmotionCount,
// [property: JsonPropertyName("height")] int Height,
// [property: JsonPropertyName("is_video_thumbnail")] bool IsVideoThumbnail,
// [property: JsonPropertyName("photo_album_key")] string PhotoAlbumKey,
// [property: JsonPropertyName("photo_key")] string PhotoKey,
// [property: JsonPropertyName("url")] string Url,
// [property: JsonPropertyName("width")] int Width
// );
// public record NextParams(
// [property: JsonPropertyName("after")] string After,
// [property: JsonPropertyName("band_key")] string BandKey,
// [property: JsonPropertyName("photo_album_key")] string PhotoAlbumKey
// );
// public record Paging(
// [property: JsonPropertyName("next_params")] NextParams NextParams,
// [property: JsonPropertyName("previous_params")] object PreviousParams
// );
// public record ResultData(
// [property: JsonPropertyName("items")] IReadOnlyList<Item> Items,
// [property: JsonPropertyName("paging")] Paging Paging
// );
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );

View File

@ -0,0 +1,45 @@
// https://developers.band.us/develop/guide/api/handle_errors
// Handle Errors
// result_code when it fails
// result_code when it fails
// result_code|message|description|resolution
// 211|Invalid Parameters||
// 212|Insufficient Parameters||Check for required parameters and add it (if applicable).
// 1001|App quota has been exceeded|API quota exceeds the limit|
// 1002|User quota has been exceeded|User quota exceeds the limit|
// 1003|Cool down time restriction|Continuous API calls for a short period of time is not available|Try again after timeout interval has expire.
// 2142|Only Band Leaders are allowed to do this|Only Band owners are allowed|
// 2300|Invalid response|Error in server response|Undefined error. Send us your client ID, request URL, and parameter info.
// 3000|Invalid request|Invalid call|Check the path and parameters and try again.
// 3001|Exceeded chareacter limit|Content length exceeds the limit|Shorten the length and try again.
// 3002|Image file size has been exceeded|Image file size exceeds the limit|Reduce the image file size and try again.
// 3003|Number of image files has been exceeded|The number of attachments exceeds the limit|Reduce the number of attachments and try again.
// 10401|Unauthorized|Authentication failure; no access token exists or it has expired|Get a new access token after login and try again.|
// 10403|Forbidden|Access is denied; no permissions|Check your permissions and contact Customer Center to request permission.
// 60000|Invalid Parameter|Invalid parameter is used|Check for required parameters and parameter types and try again.
// 60100|Invalid member|User does not exist|
// 60101|Not my friend|User is not found in the friend list|
// 60102|Not Band member|The API called is only valid for Bands that a user has joined|
// 60103|This user is not connected|The API called is only valid for users whose BAND account is connected with the app|
// 60104|This user has already been connected|The API called is only valid for users whose BAND account is not connected with the app|
// 60105|You are Band Leader. Band has members.|Band owner tries to leave a Band|
// 60106|This function is granted to the specified member.|Only specific members are given permission to use this function|
// 60200|This Band is invalid or not connected.|Band does not exist or is invalid|
// 60201|You have already joined Band|The API called is only valid for Bands that a user has not joined|
// 60202|Exceeded Band Max.|Exceeded the maximum number of Bands a user is allowed to join|
// 60203|The API called is valid for either guild Bands or official Bands||
// 60204|This Band did not allow access by the user.|Try to access a Band that is not accessible|
// 60300|Receiving message is blocked|Message is not accepted by the recipient|
// 60301|Invalid message format||
// 60302|Message service failure||
// 60400|Only designated member(s) can write post|No write permission|
// 60401|Post is not connected with the app||
// 60402|Post is not editable; images or its sub posts may be deleted.||
// 60700|This invitation is invalid|Invalid invitation|
// 60800|Image URL is invalid or the format is not supported|Invalid image URL format|
// 60801|Album not exists|Album ID does not exist|
// result_data when it fails
// result_data when it fails
// name|type|nullable|description
// message|string|N|Error message
// message|string|Y|Error details

View File

@ -0,0 +1,35 @@
// https://developers.band.us/develop/guide/api/write_post
// Request
// URL
// [POST] https://openapi.band.us/v2.2/band/post/create
// Parameters
// Parameters
// Name|Type|Mandatory|Description
// access_token|string|Y|Access token of the user
// band_key|string|Y|Band ID
// content|string|Y|Body content
// do_push|boolean|N|Sends a push notifications to all members who have subscribed to it
// Response
// response body (json)
// response body (json)
// Name|Type|Description
// result_code|integer|Result code (success: 1)
// result_data.band_key|string|Band ID
// result_data.post_key|string|Post ID
// {
// "result_code": 1,
// "result_data": {
// "band_key": "4u4i3333",
// "post_key": "34512"
// }
// }
// public record ResultData(
// [property: JsonPropertyName("band_key")] string BandKey,
// [property: JsonPropertyName("post_key")] string PostKey
// );
// public record Root(
// [property: JsonPropertyName("result_code")] int ResultCode,
// [property: JsonPropertyName("result_data")] ResultData ResultData
// );

View File

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Band.Models.Permissions;
public record ResultData(
[property: JsonPropertyName("permission")] IReadOnlyList<string> Permissions
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ResultData))]
public partial class PermissionsResultDataSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Permissions;
public record Root(
[property: JsonPropertyName("result_code")] int ResultCode,
[property: JsonPropertyName("result_data")] ResultData ResultData
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
public partial class PermissionsRootSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace Band.Models.Photos;
public record Author(
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("profile_image_url")] string ProfileImageUrl
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Author))]
public partial class PhotosAuthorSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace Band.Models.Photos;
public record Item(
[property: JsonPropertyName("author")] Author Author,
[property: JsonPropertyName("comment_count")] int CommentCount,
[property: JsonPropertyName("created_at")] object CreatedAt,
[property: JsonPropertyName("emotion_count")] int EmotionCount,
[property: JsonPropertyName("height")] int Height,
[property: JsonPropertyName("is_video_thumbnail")] bool IsVideoThumbnail,
[property: JsonPropertyName("photo_album_key")] string PhotoAlbumKey,
[property: JsonPropertyName("photo_key")] string PhotoKey,
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("width")] int Width
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Item))]
public partial class PhotosItemSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace Band.Models.Photos;
public record NextParams(
[property: JsonPropertyName("after")] string After,
[property: JsonPropertyName("band_key")] string BandKey,
[property: JsonPropertyName("photo_album_key")] string PhotoAlbumKey
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(NextParams))]
public partial class PhotosNextParamsSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Photos;
public record Paging(
[property: JsonPropertyName("next_params")] NextParams NextParams,
[property: JsonPropertyName("previous_params")] object PreviousParams
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Paging))]
public partial class PhotosPagingSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace Band.Models.Photos;
public record Photo(
[property: JsonPropertyName("author")] Author Author,
[property: JsonPropertyName("comment_count")] int CommentCount,
[property: JsonPropertyName("created_at")] object CreatedAt,
[property: JsonPropertyName("emotion_count")] int EmotionCount,
[property: JsonPropertyName("height")] int Height,
[property: JsonPropertyName("is_video_thumbnail")] bool IsVideoThumbnail,
[property: JsonPropertyName("photo_album_key")] object PhotoAlbumKey,
[property: JsonPropertyName("photo_key")] string PhotoKey,
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("width")] int Width
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Photo))]
public partial class PhotosPhotoSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Photos;
public record ResultData(
[property: JsonPropertyName("items")] IReadOnlyList<Item> Items,
[property: JsonPropertyName("paging")] Paging Paging
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ResultData))]
public partial class PhotosResultDataSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Photos;
public record Root(
[property: JsonPropertyName("result_code")] int ResultCode,
[property: JsonPropertyName("result_data")] ResultData ResultData
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
public partial class PhotosRootSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Band.Models.Post;
public record Author(
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("profile_image_url")] string ProfileImageUrl,
[property: JsonPropertyName("role")] string Role,
[property: JsonPropertyName("user_key")] string UserKey
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Author))]
public partial class PostAuthorSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace Band.Models.Post;
public record P175284037(
[property: JsonPropertyName("author")] Author Author,
[property: JsonPropertyName("comment_count")] int CommentCount,
[property: JsonPropertyName("created_at")] long CreatedAt,
[property: JsonPropertyName("emotion_count")] int EmotionCount,
[property: JsonPropertyName("height")] int Height,
[property: JsonPropertyName("is_video_thumbnail")] bool IsVideoThumbnail,
[property: JsonPropertyName("photo_album_key")] object PhotoAlbumKey,
[property: JsonPropertyName("photo_key")] string PhotoKey,
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("width")] int Width
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(P175284037))]
public partial class PostP175284037SourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace Band.Models.Post;
public record P5110026620(
[property: JsonPropertyName("author")] Author Author,
[property: JsonPropertyName("comment_count")] int CommentCount,
[property: JsonPropertyName("created_at")] long CreatedAt,
[property: JsonPropertyName("emotion_count")] int EmotionCount,
[property: JsonPropertyName("height")] int Height,
[property: JsonPropertyName("is_video_thumbnail")] bool IsVideoThumbnail,
[property: JsonPropertyName("photo_album_key")] object PhotoAlbumKey,
[property: JsonPropertyName("photo_key")] string PhotoKey,
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("width")] int Width
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(P5110026620))]
public partial class PostP5110026620SourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Band.Models.Post;
public record Photo(
[property: JsonPropertyName("5110026620")] P5110026620 P5110026620
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Photo))]
public partial class PostPhotoSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,23 @@
using System.Text.Json.Serialization;
namespace Band.Models.Post;
public record Post(
[property: JsonPropertyName("author")] Author Author,
[property: JsonPropertyName("band_key")] string BandKey,
[property: JsonPropertyName("comment_count")] int CommentCount,
[property: JsonPropertyName("content")] string Content,
[property: JsonPropertyName("created_at")] long CreatedAt,
[property: JsonPropertyName("emotion_count")] int EmotionCount,
[property: JsonPropertyName("is_multilingual")] bool IsMultilingual,
[property: JsonPropertyName("photo")] Photo Photo,
[property: JsonPropertyName("post_key")] string PostKey,
[property: JsonPropertyName("post_read_count")] int PostReadCount,
[property: JsonPropertyName("shared_count")] int SharedCount,
[property: JsonPropertyName("video")] Video Video
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Post))]
public partial class PostPostSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Band.Models.Post;
public record ResultData(
[property: JsonPropertyName("post")] Post Post
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ResultData))]
public partial class PostResultDataSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Post;
public record Root(
[property: JsonPropertyName("result_code")] int ResultCode,
[property: JsonPropertyName("result_data")] ResultData ResultData
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
public partial class PostRootSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Band.Models.Post;
public record Video(
[property: JsonPropertyName("175284037")] P175284037 P175284037
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Video))]
public partial class PostVideoSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace Band.Models.Posts;
public record Author(
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("profile_image_url")] string ProfileImageUrl
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Author))]
public partial class PostsAuthorSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,17 @@
using System.Text.Json.Serialization;
namespace Band.Models.Posts;
public record Item(
[property: JsonPropertyName("author")] Author Author,
[property: JsonPropertyName("comment_count")] int CommentCount,
[property: JsonPropertyName("content")] string Content,
[property: JsonPropertyName("created_at")] long CreatedAt,
[property: JsonPropertyName("photos")] IReadOnlyList<Photo> Photos,
[property: JsonPropertyName("post_key")] string PostKey
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Item))]
public partial class PostsItemSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace Band.Models.Posts;
public record NextParams(
[property: JsonPropertyName("access_token")] string AccessToken,
[property: JsonPropertyName("after")] string After,
[property: JsonPropertyName("band_key")] string BandKey,
[property: JsonPropertyName("limit")] string Limit
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(NextParams))]
public partial class PostsNextParamsSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Posts;
public record Paging(
[property: JsonPropertyName("next_params")] NextParams NextParams,
[property: JsonPropertyName("previous_params")] object PreviousParams
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Paging))]
public partial class PostsPagingSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace Band.Models.Posts;
public record Photo(
[property: JsonPropertyName("author")] Author Author,
[property: JsonPropertyName("comment_count")] int CommentCount,
[property: JsonPropertyName("created_at")] object CreatedAt,
[property: JsonPropertyName("emotion_count")] int EmotionCount,
[property: JsonPropertyName("height")] int Height,
[property: JsonPropertyName("is_video_thumbnail")] bool IsVideoThumbnail,
[property: JsonPropertyName("photo_album_key")] object PhotoAlbumKey,
[property: JsonPropertyName("photo_key")] string PhotoKey,
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("width")] int Width
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Photo))]
public partial class PostsPhotoSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Posts;
public record ResultData(
[property: JsonPropertyName("items")] IReadOnlyList<Item> Items,
[property: JsonPropertyName("paging")] Paging Paging
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ResultData))]
public partial class PostsResultDataSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Posts;
public record Root(
[property: JsonPropertyName("result_code")] int ResultCode,
[property: JsonPropertyName("result_data")] ResultData ResultData
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
public partial class PostsRootSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Band.Models.Profile;
public record ResultData(
[property: JsonPropertyName("is_app_member")] bool IsAppMember,
[property: JsonPropertyName("message_allowed")] bool MessageAllowed,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("profile_image_url")] string ProfileImageUrl,
[property: JsonPropertyName("user_key")] string UserKey
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ResultData))]
public partial class ProfileResultDataSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models.Profile;
public record Root(
[property: JsonPropertyName("result_code")] int ResultCode,
[property: JsonPropertyName("result_data")] ResultData ResultData
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
public partial class ProfileRootSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models;
public record ResultData(
[property: JsonPropertyName("band_key")] string BandKey,
[property: JsonPropertyName("post_key")] string PostKey
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ResultData))]
public partial class ResultDataSourceGenerationContext : JsonSerializerContext {
}

13
src/Band/Models/Root.cs Normal file
View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Band.Models;
public record Root(
[property: JsonPropertyName("result_code")] int ResultCode,
[property: JsonPropertyName("result_data")] ResultData ResultData
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
public partial class RootSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,18 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Band.Models;
public record Settings(Band? Band) {
public override string ToString() {
string result = JsonSerializer.Serialize(this, SettingsSourceGenerationContext.Default.Settings);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Settings))]
public partial class SettingsSourceGenerationContext : JsonSerializerContext {
}

17
src/Band/Models/Token.cs Normal file
View File

@ -0,0 +1,17 @@
using System.Text.Json.Serialization;
namespace Band.Models;
public record Token(
[property: JsonPropertyName("access_token")] string AccessToken,
[property: JsonPropertyName("expires_in")] int ExpiresIn,
[property: JsonPropertyName("refresh_token")] string RefreshToken,
[property: JsonPropertyName("scope")] string Scope,
[property: JsonPropertyName("token_type")] string TokenType,
[property: JsonPropertyName("user_key")] string UserKey
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Token))]
public partial class TokenSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,7 @@
namespace Band.Persistence.Database;
public static class DbConstants {
public const string DefaultConnectionStringPath = "Database:ConnectionStrings:DefaultConnection";
}

24
src/Band/Program.cs Normal file
View File

@ -0,0 +1,24 @@
using Band.DependencyInjection;
using Band.Models;
using Band.RequestPipeline;
namespace Band;
public class Program {
public static void Main(string[] args) {
Dictionary<string, Token> tokens = [];
WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args);
_ = webApplicationBuilder.Configuration.AddUserSecrets<Program>();
AppSettings appSettings = AppSettings.Get(webApplicationBuilder.Configuration);
_ = webApplicationBuilder.Services.AddServices(appSettings, tokens);
WebApplication webApplication = webApplicationBuilder.Build();
ILogger<Program>? logger = webApplication.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Starting Web Application");
_ = webApplication.InitializeCorsAndHttps(appSettings);
_ = webApplication.MapControllers();
_ = webApplication.InitializeDatabase();
webApplication.Run();
}
}

View File

@ -0,0 +1,21 @@
using Band.Models;
namespace Band.RequestPipeline;
public static class WebApplicationExtensions {
public static WebApplication InitializeDatabase(this WebApplication webApplication) =>
// DBInitializer.Initialize(application.Configuration[DbConstants.DefaultConnectionStringPath]!);
webApplication;
public static WebApplication InitializeCorsAndHttps(this WebApplication webApplication, AppSettings appSettings) {
_ = webApplication.UseCors(corsPolicyBuilder =>
corsPolicyBuilder.WithOrigins(appSettings.WithOrigins).AllowAnyHeader().AllowAnyMethod());
if (appSettings.URLs.Contains("https", StringComparison.InvariantCultureIgnoreCase)) {
_ = webApplication.UseHttpsRedirection();
_ = webApplication.UseHsts();
}
return webApplication;
}
}

View File

@ -0,0 +1,313 @@
using Band.Models;
using System.Text.Json;
namespace Band.Services;
public class BandService(ILogger<Program> logger, AppSettings appSettings, IHttpClientFactory httpClientFactory) {
#pragma warning disable CS9124
private readonly ILogger<Program> _Logger = logger;
private readonly Models.Band _Band = appSettings.Settings.Band ?? throw new Exception();
private readonly IHttpClientFactory _HttpClientFactory = httpClientFactory;
#pragma warning restore CS9124
public string? GetAuthorizationCode() {
string? result;
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetAuthorizationCode(_Logger, _Band, httpClient);
httpClient.Dispose();
return result;
}
private static string? GetAuthorizationCode(ILogger<Program> logger, Models.Band band, HttpClient httpClient) {
string? result;
Uri requestUri = new($"https://auth.band.us/oauth2/authorize?response_type=code&client_id={band.ClientId}&redirect_uri={band.Redirect}");
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(requestUri);
result = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.Redirect);
return result;
}
public Token? GetToken(string code) {
Token? result;
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetToken(_Logger, _Band, code, httpClient);
httpClient.Dispose();
return result;
}
private static Token? GetToken(ILogger<Program> logger, Models.Band band, string code, HttpClient httpClient) {
Token? result;
Uri requestUri = new($"https://auth.band.us/oauth2/token?grant_type=authorization_code&code={code}");
HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, requestUri);
httpRequestMessage.Headers.Add("Authorization", $"Basic {band.BasicAuthorization}");
Task<HttpResponseMessage> httpResponseMessage = httpClient.SendAsync(httpRequestMessage);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, TokenSourceGenerationContext.Default.Token);
return result;
}
public Models.Bands.Root? GetBands(Dictionary<string, Token>? tokens) {
Models.Bands.Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetBands(_Logger, httpClient, token);
httpClient.Dispose();
}
return result;
}
private static Token? GetToken(Dictionary<string, Token>? tokens) =>
tokens is null || tokens.Count == 0 ? null : tokens.ElementAt(0).Value;
private static string? GetString(ILogger<Program> logger, Uri requestUri, Task<HttpResponseMessage> httpResponseMessage, System.Net.HttpStatusCode httpStatusCode) {
string? result;
httpResponseMessage.Wait();
if (httpResponseMessage.Result.StatusCode != httpStatusCode) {
result = null;
logger.LogWarning("{StatusCode}: {requestUri}", httpResponseMessage.Result.StatusCode, requestUri.OriginalString);
} else {
Task<string> content = httpResponseMessage.Result.Content.ReadAsStringAsync();
content.Wait();
result = content.Result;
logger.LogDebug("{content}", result);
httpResponseMessage.Dispose();
content.Dispose();
}
return result;
}
private static Models.Bands.Root? GetBands(ILogger<Program> logger, HttpClient httpClient, Token token) {
Models.Bands.Root? result;
Uri requestUri = new($"{httpClient.BaseAddress}v2.1/bands?access_token={token.AccessToken}");
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(requestUri);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, Models.Bands.BandsRootSourceGenerationContext.Default.Root);
return result;
}
public Models.Profile.Root? GetProfile(Dictionary<string, Token>? tokens, string bandKey) {
Models.Profile.Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetProfile(_Logger, httpClient, token, bandKey);
httpClient.Dispose();
}
return result;
}
private static Models.Profile.Root? GetProfile(ILogger<Program> logger, HttpClient httpClient, Token token, string bandKey) {
Models.Profile.Root? result;
Uri requestUri = new($"{httpClient.BaseAddress}v2/profile?access_token={token.AccessToken}&band_key={bandKey}");
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(requestUri);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, Models.Profile.ProfileRootSourceGenerationContext.Default.Root);
return result;
}
public Models.Posts.Root? GetPosts(Dictionary<string, Token>? tokens, string bandKey) {
Models.Posts.Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetPosts(_Logger, httpClient, token, bandKey);
httpClient.Dispose();
}
return result;
}
private static Models.Posts.Root? GetPosts(ILogger<Program> logger, HttpClient httpClient, Token token, string bandKey) {
Models.Posts.Root? result;
Uri requestUri = new($"{httpClient.BaseAddress}v2/band/posts?access_token={token.AccessToken}&band_key={bandKey}&locale=en_US");
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(requestUri);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, Models.Posts.PostsRootSourceGenerationContext.Default.Root);
return result;
}
public Models.Post.Root? GetPost(Dictionary<string, Token>? tokens, string bandKey, string postKey) {
Models.Post.Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetPost(_Logger, httpClient, token, bandKey, postKey);
httpClient.Dispose();
}
return result;
}
private static Models.Post.Root? GetPost(ILogger<Program> logger, HttpClient httpClient, Token token, string bandKey, string postKey) {
Models.Post.Root? result;
Uri requestUri = new($"{httpClient.BaseAddress}v2/band/post?access_token={token.AccessToken}&band_key={bandKey}&post_key={postKey}");
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(requestUri);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, Models.Post.PostRootSourceGenerationContext.Default.Root);
return result;
}
public Models.Comments.Root? GetComments(Dictionary<string, Token>? tokens, string bandKey, string postKey, string sort) {
Models.Comments.Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetComments(_Logger, httpClient, token, bandKey, postKey, sort);
httpClient.Dispose();
}
return result;
}
private static Models.Comments.Root? GetComments(ILogger<Program> logger, HttpClient httpClient, Token token, string bandKey, string postKey, string sort) {
Models.Comments.Root? result;
Uri requestUri = new($"{httpClient.BaseAddress}v2/band/post/comments?access_token={token.AccessToken}&band_key={bandKey}&post_key={postKey}&sort={sort}");
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(requestUri);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, Models.Comments.CommentsRootSourceGenerationContext.Default.Root);
return result;
}
public Models.Albums.Root? GetAlbums(Dictionary<string, Token>? tokens, string bandKey) {
Models.Albums.Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetAlbums(_Logger, httpClient, token, bandKey);
httpClient.Dispose();
}
return result;
}
private static Models.Albums.Root? GetAlbums(ILogger<Program> logger, HttpClient httpClient, Token token, string bandKey) {
Models.Albums.Root? result;
Uri requestUri = new($"{httpClient.BaseAddress}v2/band/albums?access_token={token.AccessToken}&band_key={bandKey}");
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(requestUri);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, Models.Albums.AlbumsRootSourceGenerationContext.Default.Root);
return result;
}
public Models.Photos.Root? GetPhotos(Dictionary<string, Token>? tokens, string bandKey, string photoAlbumKey) {
Models.Photos.Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetPhotos(_Logger, httpClient, token, bandKey, photoAlbumKey);
httpClient.Dispose();
}
return result;
}
private static Models.Photos.Root? GetPhotos(ILogger<Program> logger, HttpClient httpClient, Token token, string bandKey, string photoAlbumKey) {
Models.Photos.Root? result;
Uri requestUri = new($"{httpClient.BaseAddress}v2/band/album/photos?access_token={token.AccessToken}&band_key={bandKey}&photo_album_key={photoAlbumKey}");
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(requestUri);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, Models.Photos.PhotosRootSourceGenerationContext.Default.Root);
return result;
}
public Models.Permissions.Root? GetPermissions(Dictionary<string, Token>? tokens, string bandKey, string permissions) {
Models.Permissions.Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetPermissions(_Logger, httpClient, token, bandKey, permissions);
httpClient.Dispose();
}
return result;
}
private static Models.Permissions.Root? GetPermissions(ILogger<Program> logger, HttpClient httpClient, Token token, string bandKey, string permissions) {
Models.Permissions.Root? result;
Uri requestUri = new($"{httpClient.BaseAddress}v2/band/permissions?access_token={token.AccessToken}&band_key={bandKey}&permissions={permissions}");
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(requestUri);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, Models.Permissions.PermissionsRootSourceGenerationContext.Default.Root);
return result;
}
public Root? WritePost(Dictionary<string, Token>? tokens, string bandKey, string message) {
Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetPostCreate(_Logger, httpClient, token, bandKey, message);
httpClient.Dispose();
}
return result;
}
private static Root? GetPostCreate(ILogger<Program> logger, HttpClient httpClient, Token token, string bandKey, string message) {
Root? result;
List<KeyValuePair<string, string>> formData = [
new KeyValuePair<string, string>("access_token", token.AccessToken),
new KeyValuePair<string, string>("band_key", string.Concat(bandKey)),
new KeyValuePair<string, string>("content", message),
new KeyValuePair<string, string>("do_push", "true")
];
FormUrlEncodedContent formContent = new(formData);
Uri requestUri = new($"{httpClient.BaseAddress}v2.2/band/post/create");
Task<HttpResponseMessage> httpResponseMessage = httpClient.PostAsync(requestUri, formContent);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, RootSourceGenerationContext.Default.Root);
return result;
}
public Root? WritePostComment(Dictionary<string, Token>? tokens, string bandKey, string postKey, string message) {
Root? result;
Token? token = GetToken(tokens);
if (token is null) {
_Logger.LogWarning("Unable to get token");
result = null;
} else {
HttpClient httpClient = _HttpClientFactory.CreateClient(nameof(AppSettings.Settings.Band));
result = GetPostCommentCreate(_Logger, httpClient, token, bandKey, postKey, message);
httpClient.Dispose();
}
return result;
}
private static Root? GetPostCommentCreate(ILogger<Program> logger, HttpClient httpClient, Token token, string bandKey, string postKey, string message) {
Root? result;
List<KeyValuePair<string, string>> formData = [
new KeyValuePair<string, string>("access_token", token.AccessToken),
new KeyValuePair<string, string>("band_key", string.Concat(bandKey)),
new KeyValuePair<string, string>("post_key", postKey),
new KeyValuePair<string, string>("body", message)
];
FormUrlEncodedContent formContent = new(formData);
Uri requestUri = new($"{httpClient.BaseAddress}v2/band/post/comment/create");
Task<HttpResponseMessage> httpResponseMessage = httpClient.PostAsync(requestUri, formContent);
string? content = GetString(logger, requestUri, httpResponseMessage, System.Net.HttpStatusCode.OK);
result = content is null ? null : JsonSerializer.Deserialize(content, RootSourceGenerationContext.Default.Root);
return result;
}
}