Compare commits
No commits in common. "v1.1.2" and "master" have entirely different histories.
37
.build.yml
Normal file
37
.build.yml
Normal file
|
@ -0,0 +1,37 @@
|
|||
image: fedora/latest
|
||||
packages:
|
||||
- dotnet
|
||||
- wget
|
||||
- unzip
|
||||
sources:
|
||||
- https://git.sr.ht/~jkcclemens/NoSoliciting
|
||||
secrets:
|
||||
- 92fe0dd0-db40-41e0-903a-a18489f75548
|
||||
tasks:
|
||||
- download-dalamud: |
|
||||
mkdir dalamud
|
||||
cd dalamud
|
||||
wget https://github.com/goatcorp/dalamud-distrib/raw/main/latest.zip
|
||||
unzip latest.zip
|
||||
rm latest.zip
|
||||
- build-plugin: |
|
||||
cd NoSoliciting/NoSoliciting
|
||||
dotnet build -c Release -p:IsCI=true
|
||||
- build-trainer: |
|
||||
cd NoSoliciting/NoSoliciting.Trainer
|
||||
dotnet build -c Release
|
||||
- test: |
|
||||
cd NoSoliciting/NoSoliciting.Trainer
|
||||
./bin/Release/net5/NoSoliciting.Trainer test data.csv
|
||||
- create-model: |
|
||||
cd NoSoliciting/NoSoliciting.Trainer
|
||||
./bin/Release/net5/NoSoliciting.Trainer create-model data.csv
|
||||
- update-model: |
|
||||
cd NoSoliciting
|
||||
LAST_COMMIT=$(ssh -o 'StrictHostKeyChecking=accept-new' actions@warm.kitteh.space model commit)
|
||||
if [ "$LAST_COMMIT" = 'None' ] || git diff-tree --no-commit-id --name-only -r "$LAST_COMMIT"..HEAD | grep '^NoSoliciting\.Trainer/data\.csv'; then
|
||||
ssh actions@warm.kitteh.space model update "$(git rev-parse HEAD)" < NoSoliciting.Trainer/model.zip
|
||||
fi
|
||||
artifacts:
|
||||
- NoSoliciting/NoSoliciting/bin/Release/net5-windows/NoSoliciting/latest.zip
|
||||
- NoSoliciting/NoSoliciting.Trainer/model.zip
|
203
.editorconfig
203
.editorconfig
|
@ -1,203 +0,0 @@
|
|||
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||
root = true
|
||||
|
||||
# C# files
|
||||
[*.{cs,json}]
|
||||
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.cs]
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
# Organize usings
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = false
|
||||
file_header_template = unset
|
||||
|
||||
# this. and Me. preferences
|
||||
dotnet_style_qualification_for_event = true:silent
|
||||
dotnet_style_qualification_for_field = true:silent
|
||||
dotnet_style_qualification_for_method = true:silent
|
||||
dotnet_style_qualification_for_property = true:silent
|
||||
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||
dotnet_style_predefined_type_for_member_access = true:silent
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||
|
||||
# Field preferences
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all:suggestion
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_elsewhere = false:silent
|
||||
csharp_style_var_for_built_in_types = false:silent
|
||||
csharp_style_var_when_type_is_apparent = false:silent
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
|
||||
# Expression-level preferences
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_prefer_index_operator = true:suggestion
|
||||
csharp_style_prefer_range_operator = true:suggestion
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
# New line preferences
|
||||
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
|
||||
|
||||
# Indentation preferences
|
||||
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
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### Naming styles ####
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -1 +1,3 @@
|
|||
* text eol=lf
|
||||
*.dll binary
|
||||
*.png binary
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,6 +3,9 @@
|
|||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# Packaging
|
||||
pack/
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
|
|
287
LICENCE
Normal file
287
LICENCE
Normal file
|
@ -0,0 +1,287 @@
|
|||
EUROPEAN UNION PUBLIC LICENCE v. 1.2
|
||||
EUPL © the European Union 2007, 2016
|
||||
|
||||
This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
|
||||
below) which is provided under the terms of this Licence. Any use of the Work,
|
||||
other than as authorised under this Licence is prohibited (to the extent such
|
||||
use is covered by a right of the copyright holder of the Work).
|
||||
|
||||
The Work is provided under the terms of this Licence when the Licensor (as
|
||||
defined below) has placed the following notice immediately following the
|
||||
copyright notice for the Work:
|
||||
|
||||
Licensed under the EUPL
|
||||
|
||||
or has expressed by any other means his willingness to license under the EUPL.
|
||||
|
||||
1. Definitions
|
||||
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
- ‘The Licence’: this Licence.
|
||||
|
||||
- ‘The Original Work’: the work or software distributed or communicated by the
|
||||
Licensor under this Licence, available as Source Code and also as Executable
|
||||
Code as the case may be.
|
||||
|
||||
- ‘Derivative Works’: the works or software that could be created by the
|
||||
Licensee, based upon the Original Work or modifications thereof. This Licence
|
||||
does not define the extent of modification or dependence on the Original Work
|
||||
required in order to classify a work as a Derivative Work; this extent is
|
||||
determined by copyright law applicable in the country mentioned in Article 15.
|
||||
|
||||
- ‘The Work’: the Original Work or its Derivative Works.
|
||||
|
||||
- ‘The Source Code’: the human-readable form of the Work which is the most
|
||||
convenient for people to study and modify.
|
||||
|
||||
- ‘The Executable Code’: any code which has generally been compiled and which is
|
||||
meant to be interpreted by a computer as a program.
|
||||
|
||||
- ‘The Licensor’: the natural or legal person that distributes or communicates
|
||||
the Work under the Licence.
|
||||
|
||||
- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
|
||||
Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
|
||||
- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
|
||||
the Work under the terms of the Licence.
|
||||
|
||||
- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
|
||||
renting, distributing, communicating, transmitting, or otherwise making
|
||||
available, online or offline, copies of the Work or providing access to its
|
||||
essential functionalities at the disposal of any other natural or legal
|
||||
person.
|
||||
|
||||
2. Scope of the rights granted by the Licence
|
||||
|
||||
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
|
||||
sublicensable licence to do the following, for the duration of copyright vested
|
||||
in the Original Work:
|
||||
|
||||
- use the Work in any circumstance and for all usage,
|
||||
- reproduce the Work,
|
||||
- modify the Work, and make Derivative Works based upon the Work,
|
||||
- communicate to the public, including the right to make available or display
|
||||
the Work or copies thereof to the public and perform publicly, as the case may
|
||||
be, the Work,
|
||||
- distribute the Work or copies thereof,
|
||||
- lend and rent the Work or copies thereof,
|
||||
- sublicense rights in the Work or copies thereof.
|
||||
|
||||
Those rights can be exercised on any media, supports and formats, whether now
|
||||
known or later invented, as far as the applicable law permits so.
|
||||
|
||||
In the countries where moral rights apply, the Licensor waives his right to
|
||||
exercise his moral right to the extent allowed by law in order to make effective
|
||||
the licence of the economic rights here above listed.
|
||||
|
||||
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
|
||||
any patents held by the Licensor, to the extent necessary to make use of the
|
||||
rights granted on the Work under this Licence.
|
||||
|
||||
3. Communication of the Source Code
|
||||
|
||||
The Licensor may provide the Work either in its Source Code form, or as
|
||||
Executable Code. If the Work is provided as Executable Code, the Licensor
|
||||
provides in addition a machine-readable copy of the Source Code of the Work
|
||||
along with each copy of the Work that the Licensor distributes or indicates, in
|
||||
a notice following the copyright notice attached to the Work, a repository where
|
||||
the Source Code is easily and freely accessible for as long as the Licensor
|
||||
continues to distribute or communicate the Work.
|
||||
|
||||
4. Limitations on copyright
|
||||
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits from
|
||||
any exception or limitation to the exclusive rights of the rights owners in the
|
||||
Work, of the exhaustion of those rights or of other applicable limitations
|
||||
thereto.
|
||||
|
||||
5. Obligations of the Licensee
|
||||
|
||||
The grant of the rights mentioned above is subject to some restrictions and
|
||||
obligations imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
Attribution right: The Licensee shall keep intact all copyright, patent or
|
||||
trademarks notices and all notices that refer to the Licence and to the
|
||||
disclaimer of warranties. The Licensee must include a copy of such notices and a
|
||||
copy of the Licence with every copy of the Work he/she distributes or
|
||||
communicates. The Licensee must cause any Derivative Work to carry prominent
|
||||
notices stating that the Work has been modified and the date of modification.
|
||||
|
||||
Copyleft clause: If the Licensee distributes or communicates copies of the
|
||||
Original Works or Derivative Works, this Distribution or Communication will be
|
||||
done under the terms of this Licence or of a later version of this Licence
|
||||
unless the Original Work is expressly distributed only under this version of the
|
||||
Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
|
||||
(becoming Licensor) cannot offer or impose any additional terms or conditions on
|
||||
the Work or Derivative Work that alter or restrict the terms of the Licence.
|
||||
|
||||
Compatibility clause: If the Licensee Distributes or Communicates Derivative
|
||||
Works or copies thereof based upon both the Work and another work licensed under
|
||||
a Compatible Licence, this Distribution or Communication can be done under the
|
||||
terms of this Compatible Licence. For the sake of this clause, ‘Compatible
|
||||
Licence’ refers to the licences listed in the appendix attached to this Licence.
|
||||
Should the Licensee's obligations under the Compatible Licence conflict with
|
||||
his/her obligations under this Licence, the obligations of the Compatible
|
||||
Licence shall prevail.
|
||||
|
||||
Provision of Source Code: When distributing or communicating copies of the Work,
|
||||
the Licensee will provide a machine-readable copy of the Source Code or indicate
|
||||
a repository where this Source will be easily and freely available for as long
|
||||
as the Licensee continues to distribute or communicate the Work.
|
||||
|
||||
Legal Protection: This Licence does not grant permission to use the trade names,
|
||||
trademarks, service marks, or names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the copyright notice.
|
||||
|
||||
6. Chain of Authorship
|
||||
|
||||
The original Licensor warrants that the copyright in the Original Work granted
|
||||
hereunder is owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each Contributor warrants that the copyright in the modifications he/she brings
|
||||
to the Work are owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each time You accept the Licence, the original Licensor and subsequent
|
||||
Contributors grant You a licence to their contributions to the Work, under the
|
||||
terms of this Licence.
|
||||
|
||||
7. Disclaimer of Warranty
|
||||
|
||||
The Work is a work in progress, which is continuously improved by numerous
|
||||
Contributors. It is not a finished work and may therefore contain defects or
|
||||
‘bugs’ inherent to this type of development.
|
||||
|
||||
For the above reason, the Work is provided under the Licence on an ‘as is’ basis
|
||||
and without warranties of any kind concerning the Work, including without
|
||||
limitation merchantability, fitness for a particular purpose, absence of defects
|
||||
or errors, accuracy, non-infringement of intellectual property rights other than
|
||||
copyright as stated in Article 6 of this Licence.
|
||||
|
||||
This disclaimer of warranty is an essential part of the Licence and a condition
|
||||
for the grant of any rights to the Work.
|
||||
|
||||
8. Disclaimer of Liability
|
||||
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural
|
||||
persons, the Licensor will in no event be liable for any direct or indirect,
|
||||
material or moral, damages of any kind, arising out of the Licence or of the use
|
||||
of the Work, including without limitation, damages for loss of goodwill, work
|
||||
stoppage, computer failure or malfunction, loss of data or any commercial
|
||||
damage, even if the Licensor has been advised of the possibility of such damage.
|
||||
However, the Licensor will be liable under statutory product liability laws as
|
||||
far such laws apply to the Work.
|
||||
|
||||
9. Additional agreements
|
||||
|
||||
While distributing the Work, You may choose to conclude an additional agreement,
|
||||
defining obligations or services consistent with this Licence. However, if
|
||||
accepting obligations, You may act only on your own behalf and on your sole
|
||||
responsibility, not on behalf of the original Licensor or any other Contributor,
|
||||
and only if You agree to indemnify, defend, and hold each Contributor harmless
|
||||
for any liability incurred by, or claims asserted against such Contributor by
|
||||
the fact You have accepted any warranty or additional liability.
|
||||
|
||||
10. Acceptance of the Licence
|
||||
|
||||
The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
|
||||
placed under the bottom of a window displaying the text of this Licence or by
|
||||
affirming consent in any other similar way, in accordance with the rules of
|
||||
applicable law. Clicking on that icon indicates your clear and irrevocable
|
||||
acceptance of this Licence and all of its terms and conditions.
|
||||
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and
|
||||
conditions by exercising any rights granted to You by Article 2 of this Licence,
|
||||
such as the use of the Work, the creation by You of a Derivative Work or the
|
||||
Distribution or Communication by You of the Work or copies thereof.
|
||||
|
||||
11. Information to the public
|
||||
|
||||
In case of any Distribution or Communication of the Work by means of electronic
|
||||
communication by You (for example, by offering to download the Work from a
|
||||
remote location) the distribution channel or media (for example, a website) must
|
||||
at least provide to the public the information requested by the applicable law
|
||||
regarding the Licensor, the Licence and the way it may be accessible, concluded,
|
||||
stored and reproduced by the Licensee.
|
||||
|
||||
12. Termination of the Licence
|
||||
|
||||
The Licence and the rights granted hereunder will terminate automatically upon
|
||||
any breach by the Licensee of the terms of the Licence.
|
||||
|
||||
Such a termination will not terminate the licences of any person who has
|
||||
received the Work from the Licensee under the Licence, provided such persons
|
||||
remain in full compliance with the Licence.
|
||||
|
||||
13. Miscellaneous
|
||||
|
||||
Without prejudice of Article 9 above, the Licence represents the complete
|
||||
agreement between the Parties as to the Work.
|
||||
|
||||
If any provision of the Licence is invalid or unenforceable under applicable
|
||||
law, this will not affect the validity or enforceability of the Licence as a
|
||||
whole. Such provision will be construed or reformed so as necessary to make it
|
||||
valid and enforceable.
|
||||
|
||||
The European Commission may publish other linguistic versions or new versions of
|
||||
this Licence or updated versions of the Appendix, so far this is required and
|
||||
reasonable, without reducing the scope of the rights granted by the Licence. New
|
||||
versions of the Licence will be published with a unique version number.
|
||||
|
||||
All linguistic versions of this Licence, approved by the European Commission,
|
||||
have identical value. Parties can take advantage of the linguistic version of
|
||||
their choice.
|
||||
|
||||
14. Jurisdiction
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- any litigation resulting from the interpretation of this License, arising
|
||||
between the European Union institutions, bodies, offices or agencies, as a
|
||||
Licensor, and any Licensee, will be subject to the jurisdiction of the Court
|
||||
of Justice of the European Union, as laid down in article 272 of the Treaty on
|
||||
the Functioning of the European Union,
|
||||
|
||||
- any litigation arising between other parties and resulting from the
|
||||
interpretation of this License, will be subject to the exclusive jurisdiction
|
||||
of the competent court where the Licensor resides or conducts its primary
|
||||
business.
|
||||
|
||||
15. Applicable Law
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- this Licence shall be governed by the law of the European Union Member State
|
||||
where the Licensor has his seat, resides or has his registered office,
|
||||
|
||||
- this licence shall be governed by Belgian law if the Licensor has no seat,
|
||||
residence or registered office inside a European Union Member State.
|
||||
|
||||
Appendix
|
||||
|
||||
‘Compatible Licences’ according to Article 5 EUPL are:
|
||||
|
||||
- GNU General Public License (GPL) v. 2, v. 3
|
||||
- GNU Affero General Public License (AGPL) v. 3
|
||||
- Open Software License (OSL) v. 2.1, v. 3.0
|
||||
- Eclipse Public License (EPL) v. 1.0
|
||||
- CeCILL v. 2.0, v. 2.1
|
||||
- Mozilla Public Licence (MPL) v. 2
|
||||
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
|
||||
works other than software
|
||||
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
|
||||
Reciprocity (LiLiQ-R+).
|
||||
|
||||
The European Commission may update this Appendix to later versions of the above
|
||||
licences without producing a new version of the EUPL, as long as they provide
|
||||
the rights granted in Article 2 of this Licence and protect the covered Source
|
||||
Code from exclusive appropriation.
|
||||
|
||||
All other changes or additions to this Appendix require the production of a new
|
||||
EUPL version.
|
155
NoSoliciting.Interface/Data.cs
Executable file
155
NoSoliciting.Interface/Data.cs
Executable file
|
@ -0,0 +1,155 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.ML.Data;
|
||||
using Microsoft.ML.Transforms;
|
||||
|
||||
namespace NoSoliciting.Interface {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
public class Data {
|
||||
[LoadColumn(0)]
|
||||
public string? Category { get; set; }
|
||||
|
||||
[LoadColumn(1)]
|
||||
public uint Channel { get; set; }
|
||||
|
||||
[LoadColumn(2)]
|
||||
public string Message { get; set; } = null!;
|
||||
|
||||
public Data() {
|
||||
}
|
||||
|
||||
public Data(ushort channel, string message) {
|
||||
this.Channel = channel;
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
#region normalisation
|
||||
|
||||
[CustomMappingFactoryAttribute("Normalise")]
|
||||
[SuppressMessage("ReSharper", "UnusedType.Global")]
|
||||
public class Normalise : CustomMappingFactory<Data, Normalise.Normalised> {
|
||||
public override Action<Data, Normalised> GetMapping() {
|
||||
return Convert;
|
||||
}
|
||||
|
||||
private static void Convert(Data data, Normalised normalised) {
|
||||
normalised.NormalisedMessage = NoSolUtil.Normalise(data.Message, true);
|
||||
}
|
||||
|
||||
public class Normalised {
|
||||
public string? NormalisedMessage { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region computed
|
||||
|
||||
[CustomMappingFactoryAttribute("Compute")]
|
||||
[SuppressMessage("ReSharper", "UnusedType.Global")]
|
||||
public class ComputeContext : CustomMappingFactory<Data, Computed> {
|
||||
private Dictionary<string, float> Weights { get; }
|
||||
|
||||
public ComputeContext() {
|
||||
this.Weights = new Dictionary<string, float>();
|
||||
}
|
||||
|
||||
public ComputeContext(Dictionary<string, float> weights) {
|
||||
this.Weights = weights;
|
||||
}
|
||||
|
||||
private void Compute(Data data, Computed computed) {
|
||||
data.Compute(computed, this.Weights);
|
||||
}
|
||||
|
||||
public override Action<Data, Computed> GetMapping() {
|
||||
return this.Compute;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Regex[] PlotWords = {
|
||||
new(@"\bplot\b", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"\bapartment\b", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"\bapt\b", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"p.{0,2}\d", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
};
|
||||
|
||||
private static readonly Regex[] WardWords = {
|
||||
new(@"\bward\b", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"w.{0,2}\d", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
};
|
||||
|
||||
private static readonly Regex NumbersRegex = new(@"\d{1,2}.{0,2}\d{1,2}", RegexOptions.Compiled);
|
||||
|
||||
private static readonly string[] TradeWords = {
|
||||
"B> ",
|
||||
"S> ",
|
||||
"buy",
|
||||
"sell",
|
||||
"WTB",
|
||||
"WTS",
|
||||
};
|
||||
|
||||
private static readonly Regex SketchUrlRegex = new(@"\.com-\w+\.\w+", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
|
||||
public class Computed {
|
||||
public float Weight { get; set; } = 1;
|
||||
|
||||
public bool PartyFinder { get; set; }
|
||||
|
||||
public bool Shout { get; set; }
|
||||
|
||||
public bool ContainsWard { get; set; }
|
||||
|
||||
public bool ContainsPlot { get; set; }
|
||||
|
||||
public bool ContainsHousingNumbers { get; set; }
|
||||
|
||||
public bool ContainsTradeWords { get; set; }
|
||||
|
||||
public bool ContainsSketchUrl { get; set; }
|
||||
}
|
||||
|
||||
private void Compute(Computed output, IReadOnlyDictionary<string, float> weights) {
|
||||
if (this.Category != null && weights.TryGetValue(this.Category, out var weight)) {
|
||||
output.Weight = weight;
|
||||
}
|
||||
|
||||
var normalised = NoSolUtil.Normalise(this.Message);
|
||||
|
||||
output.PartyFinder = this.Channel == 0;
|
||||
output.Shout = this.Channel is 11 or 30;
|
||||
output.ContainsWard = WardWords.Any(word => word.IsMatch(normalised));
|
||||
output.ContainsPlot = PlotWords.Any(word => word.IsMatch(normalised));
|
||||
output.ContainsHousingNumbers = NumbersRegex.IsMatch(normalised);
|
||||
output.ContainsTradeWords = TradeWords.Any(word => normalised.ContainsIgnoreCase(word));
|
||||
output.ContainsSketchUrl = SketchUrlRegex.IsMatch(normalised);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public class Prediction {
|
||||
[ColumnName("PredictedLabel")]
|
||||
public string Category { get; set; } = "UNKNOWN";
|
||||
|
||||
[ColumnName("Score")]
|
||||
public float[] Probabilities { get; set; } = Array.Empty<float>();
|
||||
}
|
||||
|
||||
internal static class Ext {
|
||||
public static bool ContainsIgnoreCase(this string haystack, string needle) {
|
||||
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(haystack, needle, CompareOptions.IgnoreCase) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
9
NoSoliciting.Interface/Interface.cs
Normal file
9
NoSoliciting.Interface/Interface.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace NoSoliciting.Interface {
|
||||
public interface IClassifier : IDisposable {
|
||||
void Initialise(byte[] data);
|
||||
|
||||
string Classify(ushort channel, string message);
|
||||
}
|
||||
}
|
120
NoSoliciting.Interface/NoSolUtil.cs
Normal file
120
NoSoliciting.Interface/NoSolUtil.cs
Normal file
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NoSoliciting.Interface {
|
||||
public static class NoSolUtil {
|
||||
private static readonly Dictionary<char, string> Replacements = new() {
|
||||
// numerals
|
||||
['\ue055'] = "1",
|
||||
['\ue056'] = "2",
|
||||
['\ue057'] = "3",
|
||||
['\ue058'] = "4",
|
||||
['\ue059'] = "5",
|
||||
['\ue099'] = "10",
|
||||
['\ue09a'] = "11",
|
||||
['\ue09b'] = "12",
|
||||
['\ue09c'] = "13",
|
||||
['\ue09d'] = "14",
|
||||
['\ue09e'] = "15",
|
||||
['\ue09f'] = "16",
|
||||
['\ue0a0'] = "17",
|
||||
['\ue0a1'] = "18",
|
||||
['\ue0a2'] = "19",
|
||||
['\ue0a3'] = "20",
|
||||
['\ue0a4'] = "21",
|
||||
['\ue0a5'] = "22",
|
||||
['\ue0a6'] = "23",
|
||||
['\ue0a7'] = "24",
|
||||
['\ue0a8'] = "25",
|
||||
['\ue0a9'] = "26",
|
||||
['\ue0aa'] = "27",
|
||||
['\ue0ab'] = "28",
|
||||
['\ue0ac'] = "29",
|
||||
['\ue0ad'] = "30",
|
||||
['\ue0ae'] = "31",
|
||||
|
||||
// symbols
|
||||
['\ue0af'] = "+",
|
||||
['\ue070'] = "?",
|
||||
|
||||
// letters in other sets
|
||||
['\ue022'] = "A",
|
||||
['\ue024'] = "_A",
|
||||
['\ue0b0'] = "E",
|
||||
};
|
||||
|
||||
private const char LowestReplacement = '\ue022';
|
||||
|
||||
private static readonly char[] SpaceSymbols = {
|
||||
'/', '|',
|
||||
'(', ')',
|
||||
'[', ']',
|
||||
'{', '}',
|
||||
'<', '>',
|
||||
'=', '+',
|
||||
'.', ',', '?', '!',
|
||||
'~', '-',
|
||||
};
|
||||
|
||||
public static string Spacify(this string input) {
|
||||
return SpaceSymbols.Aggregate(input, (current, sym) => current.Replace(sym, ' '));
|
||||
}
|
||||
|
||||
public static string Normalise(string input, bool spacify = false) {
|
||||
if (input == null) {
|
||||
throw new ArgumentNullException(nameof(input), "input cannot be null");
|
||||
}
|
||||
|
||||
// replace ffxiv private use chars
|
||||
var builder = new StringBuilder(input.Length);
|
||||
foreach (var c in input) {
|
||||
if (c < LowestReplacement) {
|
||||
goto AppendNormal;
|
||||
}
|
||||
|
||||
// alphabet
|
||||
if (c >= 0xe071 && c <= 0xe08a) {
|
||||
builder.Append((char) (c - 0xe030));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 0 to 9
|
||||
if (c >= 0xe060 && c <= 0xe069) {
|
||||
builder.Append((char) (c - 0xe030));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1 to 9
|
||||
if (c >= 0xe0b1 && c <= 0xe0b9) {
|
||||
builder.Append((char) (c - 0xe080));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1 to 9 again
|
||||
if (c >= 0xe090 && c <= 0xe098) {
|
||||
builder.Append((char) (c - 0xe05f));
|
||||
continue;
|
||||
}
|
||||
|
||||
// replacements in map
|
||||
if (Replacements.TryGetValue(c, out var rep)) {
|
||||
builder.Append(rep);
|
||||
continue;
|
||||
}
|
||||
|
||||
AppendNormal:
|
||||
builder.Append(c);
|
||||
}
|
||||
|
||||
input = builder.ToString();
|
||||
|
||||
// NFKD unicode normalisation
|
||||
var normalised = input.Normalize(NormalizationForm.FormKD);
|
||||
|
||||
// replace several symbols with spaces instead
|
||||
return spacify ? normalised.Spacify() : normalised;
|
||||
}
|
||||
}
|
||||
}
|
13
NoSoliciting.Interface/NoSoliciting.Interface.csproj
Executable file
13
NoSoliciting.Interface/NoSoliciting.Interface.csproj
Executable file
|
@ -0,0 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ML" Version="2.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
1
NoSoliciting.Trainer/.gitignore
vendored
Normal file
1
NoSoliciting.Trainer/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
model.zip
|
20
NoSoliciting.Trainer/NoSoliciting.Trainer.csproj
Executable file
20
NoSoliciting.Trainer/NoSoliciting.Trainer.csproj
Executable file
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ConsoleTables" Version="2.5.0"/>
|
||||
<PackageReference Include="CsvHelper" Version="30.0.1"/>
|
||||
<PackageReference Include="Microsoft.ML" Version="2.0.1"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NoSoliciting.Interface\NoSoliciting.Interface.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
273
NoSoliciting.Trainer/Program.cs
Normal file
273
NoSoliciting.Trainer/Program.cs
Normal file
|
@ -0,0 +1,273 @@
|
|||
using System;
|
||||
using System.Buffers.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ConsoleTables;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration;
|
||||
using Microsoft.ML;
|
||||
using Microsoft.ML.Data;
|
||||
using Microsoft.ML.Transforms.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using NoSoliciting.Interface;
|
||||
|
||||
namespace NoSoliciting.Trainer {
|
||||
internal static class Program {
|
||||
private static readonly string[] StopWords = {
|
||||
"discord",
|
||||
"gg",
|
||||
"twitch",
|
||||
"tv",
|
||||
"lgbt",
|
||||
"lgbtq",
|
||||
"lgbtqia",
|
||||
"http",
|
||||
"https",
|
||||
"18",
|
||||
"come",
|
||||
"join",
|
||||
"blu",
|
||||
"mounts",
|
||||
"ffxiv",
|
||||
};
|
||||
|
||||
private enum Mode {
|
||||
Test,
|
||||
CreateModel,
|
||||
Interactive,
|
||||
InteractiveFull,
|
||||
Normalise,
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
private class ReportInput {
|
||||
public uint ReportVersion { get; } = 2;
|
||||
public uint ModelVersion { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
public ushort Type { get; set; }
|
||||
public List<byte> Sender { get; set; }
|
||||
public List<byte> Content { get; set; }
|
||||
public string? Reason { get; set; }
|
||||
public string? SuggestedClassification { get; set; }
|
||||
}
|
||||
|
||||
private static void Main(string[] args) {
|
||||
var mode = args[0] switch {
|
||||
"test" => Mode.Test,
|
||||
"create-model" => Mode.CreateModel,
|
||||
"interactive" => Mode.Interactive,
|
||||
"interactive-full" => Mode.InteractiveFull,
|
||||
"normalise" => Mode.Normalise,
|
||||
_ => throw new ArgumentException("invalid argument"),
|
||||
};
|
||||
|
||||
if (mode == Mode.Normalise) {
|
||||
Console.WriteLine("Ready");
|
||||
|
||||
while (true) {
|
||||
Console.Write("> ");
|
||||
var input = Console.ReadLine();
|
||||
var bytes = Convert.FromBase64String(input!);
|
||||
var toNormalise = Encoding.UTF8.GetString(bytes);
|
||||
var normalised = NoSolUtil.Normalise(toNormalise);
|
||||
Console.WriteLine(normalised);
|
||||
}
|
||||
}
|
||||
|
||||
var path = "../../../data.csv";
|
||||
|
||||
if (args.Length > 1) {
|
||||
path = args[1];
|
||||
}
|
||||
|
||||
var parentDir = Directory.GetParent(path);
|
||||
if (parentDir == null) {
|
||||
throw new ArgumentException("data.csv did not have a parent directory");
|
||||
}
|
||||
|
||||
var ctx = new MLContext(1);
|
||||
|
||||
List<Data> records;
|
||||
|
||||
using (var fileStream = new FileStream(path, FileMode.Open)) {
|
||||
using var stream = new StreamReader(fileStream);
|
||||
using var csv = new CsvReader(stream, new CsvConfiguration(CultureInfo.InvariantCulture) {
|
||||
HeaderValidated = null,
|
||||
});
|
||||
records = csv
|
||||
.GetRecords<Data>()
|
||||
.Select(rec => {
|
||||
rec.Message = rec.Message
|
||||
.Replace("", "") // auto-translate start
|
||||
.Replace("", "") // auto-translate end
|
||||
.Replace("\r\n", " ")
|
||||
.Replace("\r", " ")
|
||||
.Replace("\n", " ");
|
||||
return rec;
|
||||
})
|
||||
.OrderBy(rec => rec.Category)
|
||||
.ThenBy(rec => rec.Channel)
|
||||
.ThenBy(rec => rec.Message)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
using (var fileStream = new FileStream(path, FileMode.Create)) {
|
||||
using var stream = new StreamWriter(fileStream);
|
||||
using var csv = new CsvWriter(stream, new CsvConfiguration(CultureInfo.InvariantCulture) {
|
||||
NewLine = "\n",
|
||||
});
|
||||
csv.WriteRecords(records);
|
||||
}
|
||||
|
||||
var classes = new Dictionary<string, uint>();
|
||||
|
||||
foreach (var record in records) {
|
||||
// keep track of how many message of each category we have
|
||||
if (!classes.ContainsKey(record.Category!)) {
|
||||
classes[record.Category] = 0;
|
||||
}
|
||||
|
||||
classes[record.Category] += 1;
|
||||
}
|
||||
|
||||
// calculate class weights
|
||||
var weights = new Dictionary<string, float>();
|
||||
foreach (var (category, count) in classes) {
|
||||
var nSamples = (float) records.Count;
|
||||
var nClasses = (float) classes.Count;
|
||||
var nSamplesJ = (float) count;
|
||||
|
||||
var w = nSamples / (nClasses * nSamplesJ);
|
||||
|
||||
weights[category] = w;
|
||||
}
|
||||
|
||||
var df = ctx.Data.LoadFromEnumerable(records);
|
||||
|
||||
var ttd = ctx.Data.TrainTestSplit(df, 0.2, seed: 1);
|
||||
|
||||
var compute = new Data.ComputeContext(weights);
|
||||
var normalise = new Data.Normalise();
|
||||
|
||||
ctx.ComponentCatalog.RegisterAssembly(typeof(Data).Assembly);
|
||||
|
||||
var pipeline = ctx.Transforms.Conversion.MapValueToKey("Label", nameof(Data.Category))
|
||||
.Append(ctx.Transforms.CustomMapping(compute.GetMapping(), "Compute"))
|
||||
.Append(ctx.Transforms.CustomMapping(normalise.GetMapping(), "Normalise"))
|
||||
.Append(ctx.Transforms.Text.NormalizeText("MsgNormal", nameof(Data.Normalise.Normalised.NormalisedMessage), keepPunctuations: false, keepNumbers: false))
|
||||
.Append(ctx.Transforms.Text.TokenizeIntoWords("MsgTokens", "MsgNormal"))
|
||||
.Append(ctx.Transforms.Text.RemoveDefaultStopWords("MsgNoDefStop", "MsgTokens"))
|
||||
.Append(ctx.Transforms.Text.RemoveStopWords("MsgNoStop", "MsgNoDefStop", StopWords))
|
||||
.Append(ctx.Transforms.Conversion.MapValueToKey("MsgKey", "MsgNoStop"))
|
||||
.Append(ctx.Transforms.Text.ProduceNgrams("MsgNgrams", "MsgKey", weighting: NgramExtractingEstimator.WeightingCriteria.Tf))
|
||||
.Append(ctx.Transforms.NormalizeLpNorm("FeaturisedMessage", "MsgNgrams"))
|
||||
.Append(ctx.Transforms.Conversion.ConvertType("CPartyFinder", nameof(Data.Computed.PartyFinder)))
|
||||
.Append(ctx.Transforms.Conversion.ConvertType("CShout", nameof(Data.Computed.Shout)))
|
||||
.Append(ctx.Transforms.Conversion.ConvertType("CTrade", nameof(Data.Computed.ContainsTradeWords)))
|
||||
.Append(ctx.Transforms.Conversion.ConvertType("CSketch", nameof(Data.Computed.ContainsSketchUrl)))
|
||||
.Append(ctx.Transforms.Conversion.ConvertType("HasWard", nameof(Data.Computed.ContainsWard)))
|
||||
.Append(ctx.Transforms.Conversion.ConvertType("HasPlot", nameof(Data.Computed.ContainsPlot)))
|
||||
.Append(ctx.Transforms.Conversion.ConvertType("HasNumbers", nameof(Data.Computed.ContainsHousingNumbers)))
|
||||
.Append(ctx.Transforms.Concatenate("Features", "FeaturisedMessage", "CPartyFinder", "CShout", "CTrade", "HasWard", "HasPlot", "HasNumbers", "CSketch"))
|
||||
// macro 81.8 micro 84.6 (Tf weighting) - slow
|
||||
// .Append(ctx.MulticlassClassification.Trainers.SdcaMaximumEntropy(exampleWeightColumnName: "Weight", l1Regularization: 0, l2Regularization: 0, maximumNumberOfIterations: 2_500))
|
||||
.Append(ctx.MulticlassClassification.Trainers.SdcaMaximumEntropy(exampleWeightColumnName: "Weight", l1Regularization: 0, l2Regularization: 0, maximumNumberOfIterations: null))
|
||||
.Append(ctx.Transforms.Conversion.MapKeyToValue("PredictedLabel"));
|
||||
|
||||
var train = mode switch {
|
||||
Mode.Test => ttd.TrainSet,
|
||||
Mode.CreateModel => df,
|
||||
Mode.Interactive => ttd.TrainSet,
|
||||
Mode.InteractiveFull => df,
|
||||
_ => throw new ArgumentOutOfRangeException($"mode {mode} not handled"),
|
||||
};
|
||||
|
||||
var model = pipeline.Fit(train);
|
||||
|
||||
if (mode == Mode.CreateModel) {
|
||||
var savePath = Path.Join(parentDir.FullName, "model.zip");
|
||||
ctx.Model.Save(model, train.Schema, savePath);
|
||||
}
|
||||
|
||||
var testPredictions = model.Transform(ttd.TestSet);
|
||||
var eval = ctx.MulticlassClassification.Evaluate(testPredictions);
|
||||
|
||||
var predEngine = ctx.Model.CreatePredictionEngine<Data, Prediction>(model);
|
||||
|
||||
var slotNames = new VBuffer<ReadOnlyMemory<char>>();
|
||||
predEngine.OutputSchema["Score"].GetSlotNames(ref slotNames);
|
||||
var names = slotNames.DenseValues()
|
||||
.Select(column => column.ToString())
|
||||
.ToList();
|
||||
|
||||
var cols = new string[1 + names.Count];
|
||||
cols[0] = "";
|
||||
for (var j = 0; j < names.Count; j++) {
|
||||
cols[j + 1] = names[j];
|
||||
}
|
||||
|
||||
var table = new ConsoleTable(cols);
|
||||
|
||||
for (var i = 0; i < names.Count; i++) {
|
||||
var name = names[i];
|
||||
var confuse = eval.ConfusionMatrix.Counts[i];
|
||||
|
||||
var row = new object[1 + confuse.Count];
|
||||
row[0] = name;
|
||||
for (var j = 0; j < confuse.Count; j++) {
|
||||
if (i == j) {
|
||||
row[j + 1] = $"= {confuse[j]} =";
|
||||
} else {
|
||||
row[j + 1] = confuse[j];
|
||||
}
|
||||
}
|
||||
|
||||
table.AddRow(row);
|
||||
}
|
||||
|
||||
Console.WriteLine("Rows are expected classification and columns are actual classification.");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine(table.ToString());
|
||||
|
||||
Console.WriteLine($"Log loss : {eval.LogLoss * 100}");
|
||||
Console.WriteLine($"Macro acc: {eval.MacroAccuracy * 100}");
|
||||
Console.WriteLine($"Micro acc: {eval.MicroAccuracy * 100}");
|
||||
|
||||
switch (mode) {
|
||||
case Mode.Test:
|
||||
case Mode.CreateModel:
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var msg = Console.ReadLine()!.Trim();
|
||||
|
||||
var parts = msg.Split(' ', 2);
|
||||
|
||||
if (parts.Length < 2 || !ushort.TryParse(parts[0], out var channel)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var size = Base64.GetMaxDecodedFromUtf8Length(parts[1].Length);
|
||||
var buf = new byte[size];
|
||||
if (Convert.TryFromBase64String(parts[1], buf, out var written)) {
|
||||
parts[1] = Encoding.UTF8.GetString(buf[..written]);
|
||||
}
|
||||
|
||||
var input = new Data(channel, parts[1]);
|
||||
var pred = predEngine.Predict(input);
|
||||
|
||||
Console.WriteLine(pred.Category);
|
||||
for (var i = 0; i < names.Count; i++) {
|
||||
Console.WriteLine($" {names[i]}: {pred.Probabilities[i] * 100}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
58
NoSoliciting.Trainer/XivString.cs
Executable file
58
NoSoliciting.Trainer/XivString.cs
Executable file
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NoSoliciting.Trainer {
|
||||
public static class XivString {
|
||||
private const byte Start = 2;
|
||||
private const byte End = 3;
|
||||
|
||||
public static string GetText(IEnumerable<byte> bytes) {
|
||||
var stringBytes = new List<byte>();
|
||||
|
||||
var reader = new BinaryReader(new MemoryStream(bytes.ToArray()));
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length) {
|
||||
var b = reader.ReadByte();
|
||||
if (b == Start) {
|
||||
reader.ReadByte(); // kind
|
||||
var len = GetInteger(reader); // data length
|
||||
reader.ReadBytes((int) len); // data
|
||||
var end = reader.ReadByte(); // end
|
||||
if (end != End) {
|
||||
throw new ArgumentException("Input was not a valid XivString");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
stringBytes.Add(b);
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(stringBytes.ToArray());
|
||||
}
|
||||
|
||||
// Thanks, Dalamud
|
||||
|
||||
public static uint GetInteger(BinaryReader input) {
|
||||
uint marker = input.ReadByte();
|
||||
if (marker < 0xD0) {
|
||||
return marker - 1;
|
||||
}
|
||||
|
||||
// the game adds 0xF0 marker for values >= 0xCF
|
||||
// uasge of 0xD0-0xEF is unknown, should we throw here?
|
||||
// if (marker < 0xF0) throw new NotSupportedException();
|
||||
|
||||
marker = (marker + 1) & 0b1111;
|
||||
|
||||
var ret = new byte[4];
|
||||
for (var i = 3; i >= 0; i--) {
|
||||
ret[i] = (marker & (1 << i)) == 0 ? (byte) 0 : input.ReadByte();
|
||||
}
|
||||
|
||||
return BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
}
|
6507
NoSoliciting.Trainer/data.csv
Executable file
6507
NoSoliciting.Trainer/data.csv
Executable file
File diff suppressed because it is too large
Load Diff
15
NoSoliciting.sln
Normal file → Executable file
15
NoSoliciting.sln
Normal file → Executable file
|
@ -6,9 +6,10 @@ MinimumVisualStudioVersion = 10.0.40219.1
|
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting", "NoSoliciting\NoSoliciting.csproj", "{E4C12987-9064-4788-8783-BE418B2C0142}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C424003-6609-47D7-8D27-BF309C34E909}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting.Interface", "NoSoliciting.Interface\NoSoliciting.Interface.csproj", "{E88E57AB-EFB8-4F2F-93DB-F63123638C44}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting.Trainer", "NoSoliciting.Trainer\NoSoliciting.Trainer.csproj", "{3D774127-F7A9-4B6D-AB2F-3AAF80D15586}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -20,6 +21,14 @@ Global
|
|||
{E4C12987-9064-4788-8783-BE418B2C0142}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4C12987-9064-4788-8783-BE418B2C0142}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4C12987-9064-4788-8783-BE418B2C0142}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E88E57AB-EFB8-4F2F-93DB-F63123638C44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E88E57AB-EFB8-4F2F-93DB-F63123638C44}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E88E57AB-EFB8-4F2F-93DB-F63123638C44}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E88E57AB-EFB8-4F2F-93DB-F63123638C44}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3D774127-F7A9-4B6D-AB2F-3AAF80D15586}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3D774127-F7A9-4B6D-AB2F-3AAF80D15586}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3D774127-F7A9-4B6D-AB2F-3AAF80D15586}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3D774127-F7A9-4B6D-AB2F-3AAF80D15586}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
38
NoSoliciting/Commands.cs
Executable file
38
NoSoliciting/Commands.cs
Executable file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using Dalamud.Game.Command;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public class Commands : IDisposable {
|
||||
private Plugin Plugin { get; }
|
||||
|
||||
internal Commands(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
this.Plugin.CommandManager.AddHandler("/prmt", new CommandInfo(this.OnCommand) {
|
||||
HelpMessage = "Opens the NoSoliciting configuration (deprecated)",
|
||||
ShowInHelp = false,
|
||||
});
|
||||
this.Plugin.CommandManager.AddHandler("/nosol", new CommandInfo(this.OnCommand) {
|
||||
HelpMessage = "Opens the NoSoliciting configuration",
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.CommandManager.RemoveHandler("/nosol");
|
||||
this.Plugin.CommandManager.RemoveHandler("/prmt");
|
||||
}
|
||||
|
||||
private void OnCommand(string command, string args) {
|
||||
if (command == "/prmt") {
|
||||
this.Plugin.ChatGui.PrintError($"[{Plugin.Name}] The /prmt command is deprecated and will be removed. Please use /nosol instead.");
|
||||
}
|
||||
|
||||
if (args == "report") {
|
||||
this.Plugin.Ui.Report.Toggle();
|
||||
return;
|
||||
}
|
||||
|
||||
this.Plugin.Ui.Settings.Toggle();
|
||||
}
|
||||
}
|
||||
}
|
54
NoSoliciting/ContextMenu.cs
Executable file
54
NoSoliciting/ContextMenu.cs
Executable file
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.ContextMenu;
|
||||
using NoSoliciting.Resources;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public class ContextMenu : IDisposable {
|
||||
private Plugin Plugin { get; }
|
||||
|
||||
internal ContextMenu(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
this.Plugin.DalamudContextMenu.OnOpenGameObjectContextMenu += this.OnOpenContextMenu;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.DalamudContextMenu.OnOpenGameObjectContextMenu -= this.OnOpenContextMenu;
|
||||
}
|
||||
|
||||
private void OnOpenContextMenu(GameObjectContextMenuOpenArgs args) {
|
||||
if (args.ParentAddonName != "LookingForGroup") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.ContentIdLower == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var label = Language.ReportToNoSoliciting;
|
||||
args.AddCustomItem(new GameObjectContextMenuItem(label, this.Report));
|
||||
}
|
||||
|
||||
private void Report(GameObjectContextMenuItemSelectedArgs args) {
|
||||
if (args.ContentIdLower == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var listing = this.Plugin.Common.Functions.PartyFinder.CurrentListings
|
||||
.Values
|
||||
.FirstOrDefault(listing => listing.ContentIdLower == args.ContentIdLower);
|
||||
|
||||
if (listing == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var message = this.Plugin.PartyFinderHistory.FirstOrDefault(message => message.ActorId == listing.ContentIdLower);
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.Plugin.Ui.Report.ToShowModal = message;
|
||||
}
|
||||
}
|
||||
}
|
9
NoSoliciting/DalamudPackager.targets
Normal file
9
NoSoliciting/DalamudPackager.targets
Normal file
|
@ -0,0 +1,9 @@
|
|||
<Project>
|
||||
<Target Name="PackagePlugin" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
|
||||
<DalamudPackager ProjectDir="$(ProjectDir)"
|
||||
OutputPath="$(OutputPath)"
|
||||
AssemblyName="$(AssemblyName)"
|
||||
VersionComponents="3"
|
||||
MakeZip="true"/>
|
||||
</Target>
|
||||
</Project>
|
24
NoSoliciting/Filter.Chat.cs
Normal file
24
NoSoliciting/Filter.Chat.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using NoSoliciting.Interface;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public partial class Filter {
|
||||
private static class Chat {
|
||||
public static bool MatchesCustomFilters(string msg, PluginConfiguration config) {
|
||||
if (config == null) {
|
||||
throw new ArgumentNullException(nameof(config), "PluginConfiguration cannot be null");
|
||||
}
|
||||
|
||||
if (!config.CustomChatFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
msg = NoSolUtil.Normalise(msg);
|
||||
|
||||
return config.ValidChatSubstrings.Any(needle => msg.ContainsIgnoreCase(needle))
|
||||
|| config.CompiledChatRegexes.Any(needle => needle.IsMatch(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
NoSoliciting/Filter.PartyFinder.cs
Normal file
24
NoSoliciting/Filter.PartyFinder.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using NoSoliciting.Interface;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public partial class Filter {
|
||||
private static class PartyFinder {
|
||||
public static bool MatchesCustomFilters(string msg, PluginConfiguration config) {
|
||||
if (config == null) {
|
||||
throw new ArgumentNullException(nameof(config), "PluginConfiguration cannot be null");
|
||||
}
|
||||
|
||||
if (!config.CustomPFFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
msg = NoSolUtil.Normalise(msg);
|
||||
|
||||
return config.ValidPfSubstrings.Any(needle => msg.ContainsIgnoreCase(needle))
|
||||
|| config.CompiledPfRegexes.Any(needle => needle.IsMatch(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
214
NoSoliciting/Filter.cs
Normal file
214
NoSoliciting/Filter.cs
Normal file
|
@ -0,0 +1,214 @@
|
|||
using System;
|
||||
using Dalamud.Game.Gui.PartyFinder.Types;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using NoSoliciting.Interface;
|
||||
using NoSoliciting.Ml;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public partial class Filter : IDisposable {
|
||||
private const uint MinWords = 4;
|
||||
|
||||
public static readonly ChatType[] FilteredChatTypes = {
|
||||
ChatType.Say,
|
||||
ChatType.Yell,
|
||||
ChatType.Shout,
|
||||
ChatType.TellIncoming,
|
||||
ChatType.Party,
|
||||
ChatType.CrossParty,
|
||||
ChatType.Alliance,
|
||||
ChatType.FreeCompany,
|
||||
ChatType.PvpTeam,
|
||||
ChatType.CrossLinkshell1,
|
||||
ChatType.CrossLinkshell2,
|
||||
ChatType.CrossLinkshell3,
|
||||
ChatType.CrossLinkshell4,
|
||||
ChatType.CrossLinkshell5,
|
||||
ChatType.CrossLinkshell6,
|
||||
ChatType.CrossLinkshell7,
|
||||
ChatType.CrossLinkshell8,
|
||||
ChatType.Linkshell1,
|
||||
ChatType.Linkshell2,
|
||||
ChatType.Linkshell3,
|
||||
ChatType.Linkshell4,
|
||||
ChatType.Linkshell5,
|
||||
ChatType.Linkshell6,
|
||||
ChatType.Linkshell7,
|
||||
ChatType.Linkshell8,
|
||||
ChatType.NoviceNetwork,
|
||||
};
|
||||
|
||||
private Plugin Plugin { get; }
|
||||
private int LastBatch { get; set; } = -1;
|
||||
|
||||
private bool _disposedValue;
|
||||
|
||||
public Filter(Plugin plugin) {
|
||||
this.Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
|
||||
|
||||
this.Plugin.ChatGui.CheckMessageHandled += this.OnChat;
|
||||
this.Plugin.PartyFinderGui.ReceiveListing += this.OnListing;
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing) {
|
||||
if (this._disposedValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing) {
|
||||
this.Plugin.ChatGui.CheckMessageHandled -= this.OnChat;
|
||||
this.Plugin.PartyFinderGui.ReceiveListing -= this.OnListing;
|
||||
}
|
||||
|
||||
this._disposedValue = true;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void OnChat(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) {
|
||||
isHandled = isHandled || this.FilterMessage(type, senderId, sender, message);
|
||||
}
|
||||
|
||||
private void OnListing(PartyFinderListing listing, PartyFinderListingEventArgs args) {
|
||||
try {
|
||||
if (this.LastBatch != args.BatchNumber) {
|
||||
this.Plugin.ClearPartyFinderHistory();
|
||||
}
|
||||
|
||||
this.LastBatch = args.BatchNumber;
|
||||
|
||||
var version = this.Plugin.MlFilter?.Version;
|
||||
var (category, reason) = this.MlListingFilterReason(listing);
|
||||
|
||||
this.Plugin.AddPartyFinderHistory(new Message(
|
||||
version,
|
||||
ChatType.None,
|
||||
listing.ContentIdLower,
|
||||
listing.Name,
|
||||
listing.Description,
|
||||
category,
|
||||
reason == "custom",
|
||||
reason == "ilvl",
|
||||
this.Plugin.Config.CreateFiltersClone()
|
||||
));
|
||||
|
||||
if (category == null && reason == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
args.Visible = false;
|
||||
|
||||
if (this.Plugin.Config.LogFilteredPfs) {
|
||||
Plugin.Log.Info($"Filtered PF listing from {listing.Name.TextValue} ({reason}): {listing.Description.TextValue}");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Plugin.Log.Error($"Error in PF listing event: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool FilterMessage(XivChatType type, uint senderId, SeString sender, SeString message) {
|
||||
if (message == null) {
|
||||
throw new ArgumentNullException(nameof(message), "SeString cannot be null");
|
||||
}
|
||||
|
||||
return this.MlFilterMessage(type, senderId, sender, message);
|
||||
}
|
||||
|
||||
private bool MlFilterMessage(XivChatType type, uint senderId, SeString sender, SeString message) {
|
||||
var chatType = ChatTypeExt.FromDalamud(type);
|
||||
|
||||
// NOTE: don't filter on user-controlled chat types here because custom filters are supposed to check all
|
||||
// messages except battle messages
|
||||
if (chatType.IsBattle()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var text = message.TextValue;
|
||||
|
||||
var custom = false;
|
||||
MessageCategory? classification = null;
|
||||
|
||||
// step 1. check for custom filters if enabled
|
||||
var filter = false;
|
||||
if (this.Plugin.Config.CustomChatFilter && Chat.MatchesCustomFilters(text, this.Plugin.Config)) {
|
||||
filter = true;
|
||||
custom = true;
|
||||
}
|
||||
|
||||
// only look at ml if message >= min words
|
||||
if (!filter && this.Plugin.MlFilter != null && CountWords(text) >= MinWords) {
|
||||
// step 2. classify the message using the model
|
||||
var category = this.Plugin.MlFilter.ClassifyMessage((ushort) chatType, text);
|
||||
|
||||
// step 2a. only filter if configured to act on this channel
|
||||
if (category != MessageCategory.Normal && this.Plugin.Config.MlEnabledOn(category, chatType)) {
|
||||
filter = true;
|
||||
classification = category;
|
||||
}
|
||||
}
|
||||
|
||||
var history = new Message(
|
||||
this.Plugin.MlFilter?.Version,
|
||||
ChatTypeExt.FromDalamud(type),
|
||||
senderId,
|
||||
sender,
|
||||
message,
|
||||
classification,
|
||||
custom,
|
||||
false,
|
||||
this.Plugin.Config.CreateFiltersClone()
|
||||
);
|
||||
this.Plugin.AddMessageHistory(history);
|
||||
|
||||
if (filter && this.Plugin.Config.LogFilteredChat) {
|
||||
Plugin.Log.Info($"Filtered chat message ({history.FilterReason ?? "unknown"}): {text}");
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
private (MessageCategory?, string?) MlListingFilterReason(PartyFinderListing listing) {
|
||||
if (this.Plugin.MlFilter == null) {
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
// ignore private listings if configured
|
||||
if (!this.Plugin.Config.ConsiderPrivatePfs && listing[SearchAreaFlags.Private]) {
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
// step 1. check if pf has an item level that's too high
|
||||
if (this.Plugin.Config.FilterHugeItemLevelPFs && listing.MinimumItemLevel > FilterUtil.MaxItemLevelAttainable(this.Plugin.DataManager)) {
|
||||
return (null, "ilvl");
|
||||
}
|
||||
|
||||
var desc = listing.Description.TextValue;
|
||||
|
||||
// step 2. check custom filters
|
||||
if (this.Plugin.Config.CustomPFFilter && PartyFinder.MatchesCustomFilters(desc, this.Plugin.Config)) {
|
||||
return (null, "custom");
|
||||
}
|
||||
|
||||
// only look at ml for pfs >= min words
|
||||
if (CountWords(desc) < MinWords) {
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
var category = this.Plugin.MlFilter.ClassifyMessage((ushort) ChatType.None, desc);
|
||||
|
||||
if (category != MessageCategory.Normal && this.Plugin.Config.MlEnabledOn(category, ChatType.None)) {
|
||||
return (category, Enum.GetName(category));
|
||||
}
|
||||
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
private static int CountWords(string text) {
|
||||
return text.Spacify().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Length;
|
||||
}
|
||||
}
|
||||
}
|
135
NoSoliciting/FilterUtil.cs
Normal file
135
NoSoliciting/FilterUtil.cs
Normal file
|
@ -0,0 +1,135 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin.Services;
|
||||
using NoSoliciting.Ml;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public static class FilterUtil {
|
||||
private static int MaxItemLevel { get; set; }
|
||||
|
||||
private enum Slot {
|
||||
MainHand,
|
||||
OffHand,
|
||||
Head,
|
||||
Chest,
|
||||
Hands,
|
||||
Waist,
|
||||
Legs,
|
||||
Feet,
|
||||
Earrings,
|
||||
Neck,
|
||||
Wrist,
|
||||
RingL,
|
||||
RingR,
|
||||
}
|
||||
|
||||
private static Slot? SlotFromItem(Item item) {
|
||||
var cat = item.EquipSlotCategory.Value;
|
||||
if (cat == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cat.MainHand != 0) {
|
||||
return Slot.MainHand;
|
||||
}
|
||||
|
||||
if (cat.Head != 0) {
|
||||
return Slot.Head;
|
||||
}
|
||||
|
||||
if (cat.Body != 0) {
|
||||
return Slot.Chest;
|
||||
}
|
||||
|
||||
if (cat.Gloves != 0) {
|
||||
return Slot.Hands;
|
||||
}
|
||||
|
||||
if (cat.Waist != 0) {
|
||||
return Slot.Waist;
|
||||
}
|
||||
|
||||
if (cat.Legs != 0) {
|
||||
return Slot.Legs;
|
||||
}
|
||||
|
||||
if (cat.Feet != 0) {
|
||||
return Slot.Feet;
|
||||
}
|
||||
|
||||
if (cat.OffHand != 0) {
|
||||
return Slot.OffHand;
|
||||
}
|
||||
|
||||
if (cat.Ears != 0) {
|
||||
return Slot.Earrings;
|
||||
}
|
||||
|
||||
if (cat.Neck != 0) {
|
||||
return Slot.Neck;
|
||||
}
|
||||
|
||||
if (cat.Wrists != 0) {
|
||||
return Slot.Wrist;
|
||||
}
|
||||
|
||||
if (cat.FingerL != 0) {
|
||||
return Slot.RingL;
|
||||
}
|
||||
|
||||
if (cat.FingerR != 0) {
|
||||
return Slot.RingR;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int MaxItemLevelAttainable(IDataManager data) {
|
||||
if (MaxItemLevel > 0) {
|
||||
return MaxItemLevel;
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
throw new ArgumentNullException(nameof(data), "DataManager cannot be null");
|
||||
}
|
||||
|
||||
var ilvls = new Dictionary<Slot, int>();
|
||||
|
||||
foreach (var item in data.GetExcelSheet<Item>()!) {
|
||||
var slot = SlotFromItem(item);
|
||||
if (slot == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var itemLevel = 0;
|
||||
var ilvl = item.LevelItem.Value;
|
||||
if (ilvl != null) {
|
||||
itemLevel = (int) ilvl.RowId;
|
||||
}
|
||||
|
||||
if (ilvls.TryGetValue((Slot) slot, out var currentMax) && currentMax > itemLevel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ilvls[(Slot) slot] = itemLevel;
|
||||
}
|
||||
|
||||
MaxItemLevel = (int) ilvls.Values.Average();
|
||||
|
||||
return MaxItemLevel;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Extensions {
|
||||
public static bool ContainsIgnoreCase(this string haystack, string needle) {
|
||||
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(haystack, needle, CompareOptions.IgnoreCase) >= 0;
|
||||
}
|
||||
|
||||
public static bool WasEnabled(this IEnumerable<MessageCategory> enabled, MessageCategory category) {
|
||||
return category == MessageCategory.Normal || enabled.Contains(category);
|
||||
}
|
||||
}
|
||||
}
|
4
NoSoliciting/FodyWeavers.xml
Normal file
4
NoSoliciting/FodyWeavers.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<Resourcer/>
|
||||
</Weavers>
|
|
@ -1,16 +0,0 @@
|
|||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Scope = "type", Target = "~T:NoSoliciting.PFPacket")]
|
||||
[assembly: SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Scope = "type", Target = "~T:NoSoliciting.PFListing")]
|
||||
[assembly: SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Scope = "member", Target = "~F:NoSoliciting.PFPacket.padding1")]
|
||||
[assembly: SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Scope = "member", Target = "~F:NoSoliciting.PFPacket.listings")]
|
||||
[assembly: SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Scope = "member", Target = "~F:NoSoliciting.PFListing.header")]
|
||||
[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Scope = "module")]
|
||||
[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "no", Scope = "module")]
|
||||
[assembly: SuppressMessage("Design", "CA1034:Nested types should not be visible", Scope = "module")]
|
||||
[assembly: SuppressMessage("Design", "CA1724", Scope = "module")]
|
29
NoSoliciting/Interface/PluginUi.cs
Executable file
29
NoSoliciting/Interface/PluginUi.cs
Executable file
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
|
||||
namespace NoSoliciting.Interface {
|
||||
public class PluginUi : IDisposable {
|
||||
private Plugin Plugin { get; }
|
||||
|
||||
public Settings Settings { get; }
|
||||
public Report Report { get; }
|
||||
|
||||
public PluginUi(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
this.Settings = new Settings(plugin, this);
|
||||
this.Report = new Report(plugin);
|
||||
|
||||
this.Plugin.Interface.UiBuilder.Draw += this.Draw;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.UiBuilder.Draw -= this.Draw;
|
||||
this.Settings.Dispose();
|
||||
}
|
||||
|
||||
private void Draw() {
|
||||
this.Settings.Draw();
|
||||
this.Report.Draw();
|
||||
}
|
||||
}
|
||||
}
|
456
NoSoliciting/Interface/Report.cs
Executable file
456
NoSoliciting/Interface/Report.cs
Executable file
|
@ -0,0 +1,456 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using ImGuiNET;
|
||||
using NoSoliciting.Ml;
|
||||
using NoSoliciting.Resources;
|
||||
|
||||
#if DEBUG
|
||||
using System.Text;
|
||||
#endif
|
||||
|
||||
namespace NoSoliciting.Interface {
|
||||
public class Report {
|
||||
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.Borders
|
||||
& ~ImGuiTableFlags.BordersOuterV
|
||||
| ImGuiTableFlags.PadOuterX
|
||||
| ImGuiTableFlags.RowBg
|
||||
| ImGuiTableFlags.SizingFixedFit
|
||||
| ImGuiTableFlags.ScrollY
|
||||
| ImGuiTableFlags.Hideable
|
||||
| ImGuiTableFlags.Reorderable
|
||||
| ImGuiTableFlags.Resizable;
|
||||
|
||||
private Plugin Plugin { get; }
|
||||
|
||||
private ReportStatus LastReportStatus { get; set; } = ReportStatus.None;
|
||||
|
||||
private bool _showReporting;
|
||||
|
||||
private bool ShowReporting {
|
||||
get => this._showReporting;
|
||||
set => this._showReporting = value;
|
||||
}
|
||||
|
||||
internal Message? ToShowModal { get; set; }
|
||||
|
||||
public Report(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
}
|
||||
|
||||
public void Open() {
|
||||
this.ShowReporting = true;
|
||||
}
|
||||
|
||||
public void Toggle() {
|
||||
this.ShowReporting = !this.ShowReporting;
|
||||
}
|
||||
|
||||
public void Draw() {
|
||||
var toShow = this.ToShowModal;
|
||||
if (toShow != null) {
|
||||
if (!this.SetUpReportModal(toShow)) {
|
||||
ImGui.OpenPopup($"###modal-message-{toShow.Id}");
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.ShowReporting) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.SetNextWindowSize(new Vector2(1_000, 350), ImGuiCond.FirstUseEver);
|
||||
|
||||
var windowTitle = string.Format(Language.Reporting, Plugin.Name);
|
||||
if (!ImGui.Begin($"{windowTitle}###NoSoliciting reporting", ref this._showReporting)) {
|
||||
ImGui.End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.TextUnformatted(Language.ReportHelp);
|
||||
|
||||
if (this.LastReportStatus != ReportStatus.None) {
|
||||
var status = this.LastReportStatus switch {
|
||||
ReportStatus.Failure => Language.ReportStatusFailure,
|
||||
ReportStatus.Successful => Language.ReportStatusSuccessful,
|
||||
ReportStatus.InProgress => Language.ReportStatusInProgress,
|
||||
_ => Language.ReportStatusUnknown,
|
||||
};
|
||||
var reportStatus = Language.ReportStatusMessage;
|
||||
ImGui.TextUnformatted(string.Format(reportStatus, status));
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.Spacing();
|
||||
|
||||
if (ImGui.BeginTabBar("##report-tabs")) {
|
||||
this.ChatTab();
|
||||
this.PartyFinderTab();
|
||||
|
||||
ImGui.EndTabBar();
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
private void ChatTab() {
|
||||
if (!ImGui.BeginTabItem($"{Language.ReportChatTab}###chat-report")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui.BeginChild("##chat-messages", new Vector2(-1, -1))) {
|
||||
if (ImGui.BeginTable("nosol-chat-report-table", 5, TableFlags)) {
|
||||
ImGui.TableSetupColumn(Language.ReportColumnTimestamp);
|
||||
ImGui.TableSetupColumn(Language.ReportColumnChannel);
|
||||
ImGui.TableSetupColumn(Language.ReportColumnReason);
|
||||
ImGui.TableSetupColumn(Language.ReportColumnSender);
|
||||
ImGui.TableSetupColumn(Language.ReportColumnMessage, ImGuiTableColumnFlags.WidthStretch);
|
||||
ImGui.TableSetupScrollFreeze(0, 1);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var message in this.Plugin.MessageHistory) {
|
||||
ImGui.TableNextRow();
|
||||
|
||||
if (message.Filtered) {
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(238f / 255f, 71f / 255f, 71f / 255f, 1f));
|
||||
}
|
||||
|
||||
var sender = message.Sender.Payloads
|
||||
.Where(payload => payload.Type == PayloadType.RawText)
|
||||
.Cast<TextPayload>()
|
||||
.Select(payload => payload.Text)
|
||||
.FirstOrDefault() ?? "";
|
||||
|
||||
if (AddRow(message.Timestamp.ToString(CultureInfo.CurrentCulture), message.ChatType.Name(this.Plugin.DataManager), message.FilterReason ?? string.Empty, sender, message.Content.TextValue)) {
|
||||
ImGui.OpenPopup($"###modal-message-{message.Id}");
|
||||
}
|
||||
|
||||
if (message.Filtered) {
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
this.SetUpReportModal(message);
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
private void PartyFinderTab() {
|
||||
if (!ImGui.BeginTabItem($"{Language.ReportPartyFinderTab}###pf-report")) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (ImGui.Button("Copy CSV")) {
|
||||
var builder = new StringBuilder();
|
||||
|
||||
foreach (var message in this.Plugin.PartyFinderHistory) {
|
||||
if (message.Classification == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
message.ToCsv(builder).Append('\n');
|
||||
}
|
||||
|
||||
ImGui.SetClipboardText(builder.ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ImGui.BeginChild("##pf-messages", new Vector2(-1, -1))) {
|
||||
if (ImGui.BeginTable("nosol-pf-report-table", 4, TableFlags)) {
|
||||
ImGui.TableSetupColumn(Language.ReportColumnTimestamp);
|
||||
ImGui.TableSetupColumn(Language.ReportColumnReason);
|
||||
ImGui.TableSetupColumn(Language.ReportColumnHost);
|
||||
ImGui.TableSetupColumn(Language.ReportColumnDescription, ImGuiTableColumnFlags.WidthStretch);
|
||||
ImGui.TableSetupScrollFreeze(0, 1);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var message in this.Plugin.PartyFinderHistory) {
|
||||
ImGui.TableNextRow();
|
||||
|
||||
if (message.Filtered) {
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(238f / 255f, 71f / 255f, 71f / 255f, 1f));
|
||||
}
|
||||
|
||||
var sender = message.Sender.Payloads
|
||||
.Where(payload => payload.Type == PayloadType.RawText)
|
||||
.Cast<TextPayload>()
|
||||
.Select(payload => payload.Text)
|
||||
.FirstOrDefault() ?? "";
|
||||
|
||||
if (AddRow(message.Timestamp.ToString(CultureInfo.CurrentCulture), message.FilterReason ?? string.Empty, sender, message.Content.TextValue)) {
|
||||
ImGui.OpenPopup($"###modal-message-{message.Id}");
|
||||
}
|
||||
|
||||
if (message.Filtered) {
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
this.SetUpReportModal(message);
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
#region Modal
|
||||
|
||||
private MessageCategory? _reportCategory;
|
||||
private bool _other;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns>true if modal is closing</returns>
|
||||
private bool SetUpReportModal(Message message) {
|
||||
var closing = false;
|
||||
|
||||
ImGui.SetNextWindowSize(new Vector2(350, -1));
|
||||
|
||||
var modalTitle = string.Format(Language.ReportModalTitle, Plugin.Name);
|
||||
if (!ImGui.BeginPopupModal($"{modalTitle}###modal-message-{message.Id}")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._reportCategory == null && !this._other) {
|
||||
if (message.Classification != null) {
|
||||
this._reportCategory = message.Classification;
|
||||
} else if (message.Classification == null && !message.Custom && !message.ItemLevel) {
|
||||
this._reportCategory = MessageCategory.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.PushTextWrapPos();
|
||||
|
||||
ImGui.TextUnformatted(Language.ReportModalHelp1);
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.TextUnformatted(message.Content.TextValue);
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.TextUnformatted(string.Format(Language.ReportModalOriginalClassification, message.FilterReason ?? MessageCategory.Normal.Name()));
|
||||
|
||||
ImGui.TextUnformatted(Language.ReportModalSuggestedClassification);
|
||||
|
||||
ImGui.SetNextItemWidth(-1);
|
||||
var preview = this._reportCategory?.Name() ?? (this._other ? Language.ReportModalClassificationOther : string.Empty);
|
||||
if (ImGui.BeginCombo($"##modal-classification-{message.Id}", preview)) {
|
||||
foreach (var category in (MessageCategory[]) Enum.GetValues(typeof(MessageCategory))) {
|
||||
if (ImGui.Selectable($"{category.Name()}##modal-option-{message.Id}", this._reportCategory == category)) {
|
||||
this._reportCategory = category;
|
||||
this._other = false;
|
||||
}
|
||||
|
||||
WrappedTooltip(category.Description());
|
||||
}
|
||||
|
||||
if (ImGui.Selectable(Language.ReportModalClassificationOther)) {
|
||||
this._reportCategory = null;
|
||||
this._other = true;
|
||||
}
|
||||
|
||||
WrappedTooltip(Language.ReportModalClassificationOtherDescription);
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0f, 0f, 1f));
|
||||
ImGui.TextUnformatted(Language.ReportModalHelp2);
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
string? errorText = null;
|
||||
if (message.Custom) {
|
||||
errorText = Language.ReportModalDisabledCustom;
|
||||
} else if (message.ItemLevel) {
|
||||
errorText = Language.ReportModalDisabledItemLevel;
|
||||
} else if (message.ModelVersion == null) {
|
||||
errorText = Language.ReportModalDisabledBadModel;
|
||||
} else if (!message.EnabledSnapshot.WasEnabled(this._reportCategory ?? MessageCategory.Normal)) {
|
||||
errorText = Language.ReportModalDisabledFilterNotEnabled;
|
||||
} else if (this._reportCategory == (message.Classification ?? MessageCategory.Normal)) {
|
||||
errorText = Language.ReportModalDisabledSameClassification;
|
||||
}
|
||||
|
||||
if (this._other) {
|
||||
if (ImGui.Button($"{Language.ReportModalGoToCustomButton}##report-goto-custom-{message.Id}")) {
|
||||
ImGui.CloseCurrentPopup();
|
||||
closing = true;
|
||||
if (message == this.ToShowModal) {
|
||||
this.ToShowModal = null;
|
||||
}
|
||||
|
||||
this.Plugin.Ui.Settings.ShowOtherFilters = true;
|
||||
this.Plugin.Ui.Settings.Show();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
} else if (errorText != null) {
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0f, 0f, 1f));
|
||||
ImGui.TextUnformatted(errorText);
|
||||
ImGui.PopStyleColor();
|
||||
} else {
|
||||
if (ImGui.Button($"{Language.ReportModalReport}##report-submit-{message.Id}")) {
|
||||
var suggested = this._reportCategory?.ToModelName() ?? "none (this is a bug)";
|
||||
this._reportCategory = null;
|
||||
if (message == this.ToShowModal) {
|
||||
Task.Run(async () => {
|
||||
var status = await this.Plugin.Ui.Report.ReportMessageAsync(message, suggested);
|
||||
switch (status) {
|
||||
case ReportStatus.Successful: {
|
||||
var msg = Language.ReportToastSuccess;
|
||||
this.Plugin.ToastGui.ShowNormal(string.Format(msg, message.Sender));
|
||||
break;
|
||||
}
|
||||
case ReportStatus.Failure: {
|
||||
var msg = Language.ReportToastFailure;
|
||||
this.Plugin.ToastGui.ShowError(string.Format(msg, message.Sender));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.ToShowModal = null;
|
||||
} else {
|
||||
this.ReportMessage(message, suggested);
|
||||
}
|
||||
|
||||
ImGui.CloseCurrentPopup();
|
||||
closing = true;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
}
|
||||
|
||||
if (ImGui.Button($"{Language.ReportModalCopy}##report-copy-{message.Id}")) {
|
||||
ImGui.SetClipboardText(message.Content.TextValue);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Copy CSV")) {
|
||||
ImGui.SetClipboardText(message.ToCsv().ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
var cancelButton = Language.ReportModalCancel;
|
||||
if (ImGui.Button($"{cancelButton}##report-cancel-{message.Id}")) {
|
||||
this._reportCategory = null;
|
||||
if (message == this.ToShowModal) {
|
||||
this.ToShowModal = null;
|
||||
}
|
||||
|
||||
ImGui.CloseCurrentPopup();
|
||||
closing = true;
|
||||
}
|
||||
|
||||
ImGui.PopTextWrapPos();
|
||||
|
||||
ImGui.EndPopup();
|
||||
|
||||
if (closing) {
|
||||
this._other = false;
|
||||
}
|
||||
|
||||
return closing;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utility
|
||||
|
||||
private static bool AddRow(params string[] args) {
|
||||
var clicked = false;
|
||||
|
||||
for (var i = 0; i < args.Length; i++) {
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
var arg = args[i];
|
||||
var last = i == args.Length - 1;
|
||||
|
||||
if (last) {
|
||||
ImGui.PushTextWrapPos();
|
||||
}
|
||||
|
||||
ImGui.TextUnformatted(arg);
|
||||
|
||||
if (last) {
|
||||
ImGui.PopTextWrapPos();
|
||||
}
|
||||
|
||||
clicked = clicked || ImGui.IsItemClicked();
|
||||
}
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
private static void WrappedTooltip(string text) {
|
||||
if (!ImGui.IsItemHovered()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 24);
|
||||
ImGui.TextUnformatted(text);
|
||||
ImGui.PopTextWrapPos();
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
private void ReportMessage(Message message, string suggested) {
|
||||
Task.Run(async () => await this.ReportMessageAsync(message, suggested));
|
||||
}
|
||||
|
||||
private async Task<ReportStatus> ReportMessageAsync(Message message, string suggested) {
|
||||
string? resp = null;
|
||||
try {
|
||||
using var client = new WebClient();
|
||||
this.LastReportStatus = ReportStatus.InProgress;
|
||||
var reportUrl = this.Plugin.MlFilter?.ReportUrl;
|
||||
if (reportUrl != null) {
|
||||
var json = message.ToJson(suggested);
|
||||
if (json != null) {
|
||||
resp = await client.UploadStringTaskAsync(reportUrl, json).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
} catch (Exception) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
var status = resp == "{\"message\":\"ok\"}" ? ReportStatus.Successful : ReportStatus.Failure;
|
||||
if (status == ReportStatus.Failure) {
|
||||
Plugin.Log.Warning($"Failed to report message:\n{resp}");
|
||||
}
|
||||
|
||||
this.LastReportStatus = status;
|
||||
Plugin.Log.Info(resp == null
|
||||
? "Report not sent. ML model not set."
|
||||
: $"Report sent. Response: {resp}");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
8
NoSoliciting/Interface/ReportStatus.cs
Executable file
8
NoSoliciting/Interface/ReportStatus.cs
Executable file
|
@ -0,0 +1,8 @@
|
|||
namespace NoSoliciting.Interface {
|
||||
public enum ReportStatus {
|
||||
None = -1,
|
||||
Failure = 0,
|
||||
Successful = 1,
|
||||
InProgress = 2,
|
||||
}
|
||||
}
|
368
NoSoliciting/Interface/Settings.cs
Executable file
368
NoSoliciting/Interface/Settings.cs
Executable file
|
@ -0,0 +1,368 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using NoSoliciting.Ml;
|
||||
using NoSoliciting.Resources;
|
||||
|
||||
namespace NoSoliciting.Interface {
|
||||
public class Settings : IDisposable {
|
||||
private Plugin Plugin { get; }
|
||||
private PluginUi Ui { get; }
|
||||
|
||||
private bool _showSettings;
|
||||
|
||||
private bool ShowSettings {
|
||||
get => this._showSettings;
|
||||
set => this._showSettings = value;
|
||||
}
|
||||
|
||||
public Settings(Plugin plugin, PluginUi ui) {
|
||||
this.Plugin = plugin;
|
||||
this.Ui = ui;
|
||||
|
||||
this.Plugin.Interface.UiBuilder.OpenConfigUi += this.Open;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.UiBuilder.OpenConfigUi -= this.Open;
|
||||
}
|
||||
|
||||
private void Open() {
|
||||
this.ShowSettings = true;
|
||||
}
|
||||
|
||||
public void Toggle() {
|
||||
this.ShowSettings = !this.ShowSettings;
|
||||
}
|
||||
|
||||
public void Show() {
|
||||
this.ShowSettings = true;
|
||||
}
|
||||
|
||||
public void Draw() {
|
||||
var windowTitle = string.Format(Language.Settings, Plugin.Name);
|
||||
if (!this.ShowSettings || !ImGui.Begin($"{windowTitle}###NoSoliciting settings", ref this._showSettings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var advanced = this.Plugin.Config.AdvancedMode;
|
||||
if (ImGui.Checkbox(Language.AdvancedMode, ref advanced)) {
|
||||
this.Plugin.Config.AdvancedMode = advanced;
|
||||
this.Plugin.Config.Save();
|
||||
}
|
||||
|
||||
ImGui.Spacing();
|
||||
|
||||
if (!ImGui.BeginTabBar("##nosoliciting-tabs")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.DrawMachineLearningConfig();
|
||||
|
||||
this.DrawOtherFilters();
|
||||
|
||||
this.DrawOtherTab();
|
||||
|
||||
ImGui.EndTabBar();
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (ImGui.Button(Language.ShowReportingWindow)) {
|
||||
this.Ui.Report.Open();
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
#region ML config
|
||||
|
||||
private void DrawMachineLearningConfig() {
|
||||
if (this.Plugin.Config.AdvancedMode) {
|
||||
this.DrawAdvancedMachineLearningConfig();
|
||||
} else {
|
||||
this.DrawBasicMachineLearningConfig();
|
||||
}
|
||||
|
||||
if (!ImGui.BeginTabItem($"{Language.ModelTab}###model-tab")) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.TextUnformatted(string.Format(Language.ModelTabVersion, this.Plugin.MlFilter?.Version));
|
||||
ImGui.TextUnformatted(string.Format(Language.ModelTabStatus, this.Plugin.MlStatus.Description()));
|
||||
var lastError = MlFilter.LastError;
|
||||
if (lastError != null) {
|
||||
ImGui.TextUnformatted(string.Format(Language.ModelTabError, lastError));
|
||||
}
|
||||
|
||||
if (ImGui.Button(Language.UpdateModel)) {
|
||||
// prevent issues when people spam the button
|
||||
if (ImGui.GetIO().KeyCtrl || this.Plugin.MlStatus is MlFilterStatus.Uninitialised or MlFilterStatus.Initialised) {
|
||||
this.Plugin.MlFilter?.Dispose();
|
||||
this.Plugin.MlFilter = null;
|
||||
this.Plugin.MlStatus = MlFilterStatus.Uninitialised;
|
||||
this.Plugin.InitialiseMachineLearning(ImGui.GetIO().KeyAlt);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
private void DrawBasicMachineLearningConfig() {
|
||||
if (!ImGui.BeginTabItem($"{Language.FiltersTab}###filters-tab")) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var category in MessageCategoryExt.UiOrder) {
|
||||
var check = this.Plugin.Config.BasicMlFilters.Contains(category);
|
||||
if (ImGui.Checkbox(category.Name(), ref check)) {
|
||||
if (check) {
|
||||
this.Plugin.Config.BasicMlFilters.Add(category);
|
||||
} else {
|
||||
this.Plugin.Config.BasicMlFilters.Remove(category);
|
||||
}
|
||||
|
||||
this.Plugin.Config.Save();
|
||||
}
|
||||
|
||||
if (!ImGui.IsItemHovered()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 24);
|
||||
ImGui.TextUnformatted(category.Description());
|
||||
ImGui.PopTextWrapPos();
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
private void DrawAdvancedMachineLearningConfig() {
|
||||
if (!ImGui.BeginTabItem($"{Language.FiltersTab}###filters-tab")) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(255f, 204f, 0f, 1f));
|
||||
ImGui.TextUnformatted(Language.AdvancedWarning1);
|
||||
ImGui.TextUnformatted(Language.AdvancedWarning2);
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
foreach (var category in MessageCategoryExt.UiOrder) {
|
||||
if (!ImGui.CollapsingHeader(category.Name())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.Plugin.Config.MlFilters.ContainsKey(category)) {
|
||||
this.Plugin.Config.MlFilters[category] = new HashSet<ChatType>();
|
||||
}
|
||||
|
||||
var types = this.Plugin.Config.MlFilters[category];
|
||||
|
||||
void DrawTypes(ChatType type, string id) {
|
||||
var name = type.Name(this.Plugin.DataManager);
|
||||
|
||||
var check = types.Contains(type);
|
||||
if (!ImGui.Checkbox($"{name}##{id}", ref check)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (check) {
|
||||
types.Add(type);
|
||||
} else {
|
||||
types.Remove(type);
|
||||
}
|
||||
|
||||
this.Plugin.Config.Save();
|
||||
}
|
||||
|
||||
DrawTypes(ChatType.None, category.ToString());
|
||||
|
||||
foreach (var type in Filter.FilteredChatTypes) {
|
||||
DrawTypes(type, category.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Other config
|
||||
|
||||
internal bool ShowOtherFilters;
|
||||
|
||||
private static unsafe bool BeginTabItem(string label, ImGuiTabItemFlags flags) {
|
||||
var unterminatedLabelBytes = Encoding.UTF8.GetBytes(label);
|
||||
var labelBytes = stackalloc byte[unterminatedLabelBytes.Length + 1];
|
||||
fixed (byte* unterminatedPtr = unterminatedLabelBytes) {
|
||||
Buffer.MemoryCopy(unterminatedPtr, labelBytes, unterminatedLabelBytes.Length + 1, unterminatedLabelBytes.Length);
|
||||
}
|
||||
|
||||
labelBytes[unterminatedLabelBytes.Length] = 0;
|
||||
|
||||
var num2 = (int) ImGuiNative.igBeginTabItem(labelBytes, null, flags);
|
||||
return (uint) num2 > 0U;
|
||||
}
|
||||
|
||||
private void DrawOtherFilters() {
|
||||
var flags = this.ShowOtherFilters ? ImGuiTabItemFlags.SetSelected : ImGuiTabItemFlags.None;
|
||||
this.ShowOtherFilters = false;
|
||||
|
||||
if (!BeginTabItem($"{Language.OtherFiltersTab}###other-filters-tab", flags)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader(Language.ChatFilters)) {
|
||||
var customChat = this.Plugin.Config.CustomChatFilter;
|
||||
if (ImGui.Checkbox(Language.EnableCustomChatFilters, ref customChat)) {
|
||||
this.Plugin.Config.CustomChatFilter = customChat;
|
||||
this.Plugin.Config.Save();
|
||||
}
|
||||
|
||||
if (this.Plugin.Config.CustomChatFilter) {
|
||||
var substrings = this.Plugin.Config.ChatSubstrings;
|
||||
var regexes = this.Plugin.Config.ChatRegexes;
|
||||
this.DrawCustom("chat", ref substrings, ref regexes);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader(Language.PartyFinderFilters)) {
|
||||
var filterHugeItemLevelPFs = this.Plugin.Config.FilterHugeItemLevelPFs;
|
||||
// ReSharper disable once InvertIf
|
||||
if (ImGui.Checkbox(Language.FilterIlvlPfs, ref filterHugeItemLevelPFs)) {
|
||||
this.Plugin.Config.FilterHugeItemLevelPFs = filterHugeItemLevelPFs;
|
||||
this.Plugin.Config.Save();
|
||||
}
|
||||
|
||||
var considerPrivate = this.Plugin.Config.ConsiderPrivatePfs;
|
||||
if (ImGui.Checkbox(Language.FilterPrivatePfs, ref considerPrivate)) {
|
||||
this.Plugin.Config.ConsiderPrivatePfs = considerPrivate;
|
||||
this.Plugin.Config.Save();
|
||||
}
|
||||
|
||||
var customPf = this.Plugin.Config.CustomPFFilter;
|
||||
if (ImGui.Checkbox(Language.EnableCustomPartyFinderFilters, ref customPf)) {
|
||||
this.Plugin.Config.CustomPFFilter = customPf;
|
||||
this.Plugin.Config.Save();
|
||||
}
|
||||
|
||||
if (this.Plugin.Config.CustomPFFilter) {
|
||||
var substrings = this.Plugin.Config.PFSubstrings;
|
||||
var regexes = this.Plugin.Config.PFRegexes;
|
||||
this.DrawCustom("pf", ref substrings, ref regexes);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
private void DrawCustom(string name, ref List<string> substrings, ref List<string> regexes) {
|
||||
ImGui.Columns(2);
|
||||
|
||||
ImGui.TextUnformatted(Language.SubstringsToFilter);
|
||||
if (ImGui.BeginChild($"##{name}-substrings", new Vector2(0, 175))) {
|
||||
for (var i = 0; i < substrings.Count; i++) {
|
||||
var input = substrings[i];
|
||||
if (ImGui.InputText($"##{name}-substring-{i}", ref input, 1_000)) {
|
||||
substrings[i] = input;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Trash.ToIconString()}##{name}-substring-{i}-remove")) {
|
||||
substrings.RemoveAt(i);
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconString()}##{name}-substring-add")) {
|
||||
substrings.Add("");
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
ImGui.NextColumn();
|
||||
|
||||
ImGui.TextUnformatted(Language.RegularExpressionsToFilter);
|
||||
if (ImGui.BeginChild($"##{name}-regexes", new Vector2(0, 175))) {
|
||||
for (var i = 0; i < regexes.Count; i++) {
|
||||
var input = regexes[i];
|
||||
if (ImGui.InputText($"##{name}-regex-{i}", ref input, 1_000)) {
|
||||
try {
|
||||
_ = new Regex(input);
|
||||
// update if valid
|
||||
regexes[i] = input;
|
||||
} catch (ArgumentException) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Trash.ToIconString()}##{name}-regex-{i}-remove")) {
|
||||
regexes.RemoveAt(i);
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconString()}##{name}-regex-add")) {
|
||||
regexes.Add("");
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
ImGui.Columns(1);
|
||||
|
||||
// ReSharper disable once InvertIf
|
||||
var saveLoc = Language.SaveFilters;
|
||||
if (ImGui.Button($"{saveLoc}##{name}-save")) {
|
||||
this.Plugin.Config.Save();
|
||||
this.Plugin.Config.CompileRegexes();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void DrawOtherTab() {
|
||||
if (!ImGui.BeginTabItem($"{Language.OtherTab}###other-tab")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var useGameLanguage = this.Plugin.Config.FollowGameLanguage;
|
||||
if (ImGui.Checkbox(Language.OtherGameLanguage, ref useGameLanguage)) {
|
||||
this.Plugin.Config.FollowGameLanguage = useGameLanguage;
|
||||
this.Plugin.Config.Save();
|
||||
this.Plugin.ConfigureLanguage();
|
||||
}
|
||||
|
||||
var logFilteredPfs = this.Plugin.Config.LogFilteredPfs;
|
||||
if (ImGui.Checkbox(Language.LogFilteredPfs, ref logFilteredPfs)) {
|
||||
this.Plugin.Config.LogFilteredPfs = logFilteredPfs;
|
||||
this.Plugin.Config.Save();
|
||||
}
|
||||
|
||||
var logFilteredMessages = this.Plugin.Config.LogFilteredChat;
|
||||
if (ImGui.Checkbox(Language.LogFilteredMessages, ref logFilteredMessages)) {
|
||||
this.Plugin.Config.LogFilteredChat = logFilteredMessages;
|
||||
this.Plugin.Config.Save();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
}
|
||||
}
|
253
NoSoliciting/Message.cs
Normal file
253
NoSoliciting/Message.cs
Normal file
|
@ -0,0 +1,253 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using NoSoliciting.Ml;
|
||||
|
||||
#if DEBUG
|
||||
using System.Text;
|
||||
#endif
|
||||
|
||||
namespace NoSoliciting {
|
||||
[Serializable]
|
||||
public class Message {
|
||||
public Guid Id { get; }
|
||||
|
||||
[JsonIgnore]
|
||||
public uint ActorId { get; }
|
||||
|
||||
public uint? ModelVersion { get; }
|
||||
public DateTime Timestamp { get; }
|
||||
public ChatType ChatType { get; }
|
||||
public SeString Sender { get; }
|
||||
public SeString Content { get; }
|
||||
|
||||
public IEnumerable<MessageCategory> EnabledSnapshot { get; }
|
||||
|
||||
public MessageCategory? Classification { get; }
|
||||
|
||||
public bool Custom { get; }
|
||||
public bool ItemLevel { get; }
|
||||
|
||||
public bool Filtered => this.Custom || this.ItemLevel || this.Classification != null;
|
||||
|
||||
public string? FilterReason => this.Custom
|
||||
? "custom"
|
||||
: this.ItemLevel
|
||||
? "ilvl"
|
||||
: this.Classification?.Name();
|
||||
|
||||
internal Message(uint? defsVersion, ChatType type, uint actorId, SeString sender, SeString content, MessageCategory? classification, bool custom, bool ilvl, IEnumerable<MessageCategory> enabledSnapshot) {
|
||||
this.Id = Guid.NewGuid();
|
||||
this.ModelVersion = defsVersion;
|
||||
this.Timestamp = DateTime.Now;
|
||||
this.ChatType = type;
|
||||
this.ActorId = actorId;
|
||||
this.Sender = sender;
|
||||
this.Content = content;
|
||||
this.Classification = classification;
|
||||
this.Custom = custom;
|
||||
this.ItemLevel = ilvl;
|
||||
this.EnabledSnapshot = enabledSnapshot;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
private class JsonMessage {
|
||||
public uint ReportVersion { get; } = 2;
|
||||
public uint ModelVersion { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
public ushort Type { get; set; }
|
||||
|
||||
// note: cannot use byte[] because Newtonsoft thinks it's a good idea to always base64 byte[]
|
||||
// and I don't want to write a custom converter to overwrite their stupidity
|
||||
public List<byte> Sender { get; set; }
|
||||
public List<byte> Content { get; set; }
|
||||
public string? Reason { get; set; }
|
||||
public string? SuggestedClassification { get; set; }
|
||||
}
|
||||
|
||||
public string? ToJson(string suggested) {
|
||||
if (this.ModelVersion == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var msg = new JsonMessage {
|
||||
ModelVersion = this.ModelVersion.Value,
|
||||
Timestamp = this.Timestamp,
|
||||
Type = (ushort) this.ChatType,
|
||||
Sender = this.Sender.Encode().ToList(),
|
||||
Content = this.Content.Encode().ToList(),
|
||||
Reason = this.Custom
|
||||
? "custom"
|
||||
: this.ItemLevel
|
||||
? "ilvl"
|
||||
: (this.Classification ?? MessageCategory.Normal).ToModelName(),
|
||||
SuggestedClassification = suggested,
|
||||
};
|
||||
|
||||
return JsonConvert.SerializeObject(msg, new JsonSerializerSettings {
|
||||
TypeNameHandling = TypeNameHandling.None,
|
||||
});
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public StringBuilder ToCsv(StringBuilder? builder = null) {
|
||||
builder ??= new StringBuilder();
|
||||
|
||||
builder.Append(this.Classification?.ToModelName());
|
||||
builder.Append(',');
|
||||
builder.Append((int) this.ChatType);
|
||||
builder.Append(",\"");
|
||||
builder.Append(this.Content.TextValue
|
||||
.Replace("\"", "\"\"")
|
||||
.Replace("\r", " "));
|
||||
builder.Append('"');
|
||||
|
||||
return builder;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")]
|
||||
public enum ChatType : ushort {
|
||||
None = 0,
|
||||
Debug = 1,
|
||||
Urgent = 2,
|
||||
Notice = 3,
|
||||
Say = 10,
|
||||
Shout = 11,
|
||||
TellOutgoing = 12,
|
||||
TellIncoming = 13,
|
||||
Party = 14,
|
||||
Alliance = 15,
|
||||
Linkshell1 = 16,
|
||||
Linkshell2 = 17,
|
||||
Linkshell3 = 18,
|
||||
Linkshell4 = 19,
|
||||
Linkshell5 = 20,
|
||||
Linkshell6 = 21,
|
||||
Linkshell7 = 22,
|
||||
Linkshell8 = 23,
|
||||
FreeCompany = 24,
|
||||
NoviceNetwork = 27,
|
||||
CustomEmote = 28,
|
||||
StandardEmote = 29,
|
||||
Yell = 30,
|
||||
CrossParty = 32,
|
||||
PvpTeam = 36,
|
||||
CrossLinkshell1 = 37,
|
||||
Damage = 41,
|
||||
Miss = 42,
|
||||
Action = 43,
|
||||
Item = 44,
|
||||
Healing = 45,
|
||||
GainBuff = 46,
|
||||
GainDebuff = 47,
|
||||
LoseBuff = 48,
|
||||
LoseDebuff = 49,
|
||||
Alarm = 55,
|
||||
Echo = 56,
|
||||
System = 57,
|
||||
BattleSystem = 58,
|
||||
GatheringSystem = 59,
|
||||
Error = 60,
|
||||
NpcDialogue = 61,
|
||||
LootNotice = 62,
|
||||
Progress = 64,
|
||||
LootRoll = 65,
|
||||
Crafting = 66,
|
||||
Gathering = 67,
|
||||
NpcAnnouncement = 68,
|
||||
FreeCompanyAnnouncement = 69,
|
||||
FreeCompanyLoginLogout = 70,
|
||||
RetainerSale = 71,
|
||||
PeriodicRecruitmentNotification = 72,
|
||||
Sign = 73,
|
||||
RandomNumber = 74,
|
||||
NoviceNetworkSystem = 75,
|
||||
Orchestrion = 76,
|
||||
PvpTeamAnnouncement = 77,
|
||||
PvpTeamLoginLogout = 78,
|
||||
MessageBook = 79,
|
||||
GmTell = 80,
|
||||
GmSay = 81,
|
||||
GmShout = 82,
|
||||
GmYell = 83,
|
||||
GmParty = 84,
|
||||
GmFreeComapny = 85,
|
||||
GmLinkshell1 = 86,
|
||||
GmLinkshell2 = 87,
|
||||
GmLinkshell3 = 88,
|
||||
GmLinkshell4 = 89,
|
||||
GmLinkshell5 = 90,
|
||||
GmLinkshell6 = 91,
|
||||
GmLinkshell7 = 92,
|
||||
GmLinkshell8 = 93,
|
||||
GmNoviceNetwork = 94,
|
||||
CrossLinkshell2 = 101,
|
||||
CrossLinkshell3 = 102,
|
||||
CrossLinkshell4 = 103,
|
||||
CrossLinkshell5 = 104,
|
||||
CrossLinkshell6 = 105,
|
||||
CrossLinkshell7 = 106,
|
||||
CrossLinkshell8 = 107,
|
||||
}
|
||||
|
||||
public static class ChatTypeExt {
|
||||
private const ushort Clear7 = ~(~0 << 7);
|
||||
|
||||
public static byte LogKind(this ChatType type) => type switch {
|
||||
ChatType.TellIncoming => (byte) ChatType.TellOutgoing,
|
||||
_ => (byte) type,
|
||||
};
|
||||
|
||||
public static string Name(this ChatType type, IDataManager data) {
|
||||
switch (type) {
|
||||
case ChatType.None:
|
||||
return "Party Finder";
|
||||
case ChatType.TellIncoming:
|
||||
return "Tell (Incoming)";
|
||||
case ChatType.TellOutgoing:
|
||||
return "Tell (Outgoing)";
|
||||
case ChatType.CrossParty:
|
||||
return "Party (Cross-world)";
|
||||
}
|
||||
|
||||
var lf = data.GetExcelSheet<LogFilter>()!.FirstOrDefault(lf => lf.LogKind == type.LogKind());
|
||||
return lf?.Name?.ToString() ?? type.ToString();
|
||||
}
|
||||
|
||||
public static ChatType FromCode(ushort code) {
|
||||
return (ChatType) (code & Clear7);
|
||||
}
|
||||
|
||||
public static ChatType FromDalamud(XivChatType type) {
|
||||
return FromCode((ushort) type);
|
||||
}
|
||||
|
||||
public static bool IsBattle(this ChatType type) {
|
||||
switch (type) {
|
||||
case ChatType.Damage:
|
||||
case ChatType.Miss:
|
||||
case ChatType.Action:
|
||||
case ChatType.Item:
|
||||
case ChatType.Healing:
|
||||
case ChatType.GainBuff:
|
||||
case ChatType.LoseBuff:
|
||||
case ChatType.GainDebuff:
|
||||
case ChatType.LoseDebuff:
|
||||
case ChatType.BattleSystem:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
NoSoliciting/Ml/Classifier.cs
Executable file
35
NoSoliciting/Ml/Classifier.cs
Executable file
|
@ -0,0 +1,35 @@
|
|||
using System.IO;
|
||||
using Microsoft.ML;
|
||||
using NoSoliciting.Interface;
|
||||
|
||||
namespace NoSoliciting.Ml {
|
||||
internal class Classifier : IClassifier {
|
||||
private MLContext Context { get; set; } = null!;
|
||||
private ITransformer Model { get; set; } = null!;
|
||||
private DataViewSchema Schema { get; set; } = null!;
|
||||
private PredictionEngine<Data, Prediction>? PredictionEngine { get; set; }
|
||||
|
||||
public void Initialise(byte[] data) {
|
||||
if (this.PredictionEngine != null) {
|
||||
this.PredictionEngine.Dispose();
|
||||
this.PredictionEngine = null;
|
||||
}
|
||||
|
||||
this.Context = new MLContext();
|
||||
this.Context.ComponentCatalog.RegisterAssembly(typeof(Data).Assembly);
|
||||
using var stream = new MemoryStream(data);
|
||||
var model = this.Context.Model.Load(stream, out var schema);
|
||||
this.Model = model;
|
||||
this.Schema = schema;
|
||||
this.PredictionEngine = this.Context.Model.CreatePredictionEngine<Data, Prediction>(this.Model, this.Schema);
|
||||
}
|
||||
|
||||
public string Classify(ushort channel, string message) {
|
||||
return this.PredictionEngine?.Predict(new Data(channel, message))?.Category ?? "UNKNOWN";
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.PredictionEngine?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
13
NoSoliciting/Ml/Manifest.cs
Normal file
13
NoSoliciting/Ml/Manifest.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace NoSoliciting.Ml {
|
||||
[Serializable]
|
||||
public class Manifest {
|
||||
public uint Version { get; set; }
|
||||
public Uri ModelUrl { get; set; }
|
||||
public string ModelHash { get; set; }
|
||||
public Uri ReportUrl { get; set; }
|
||||
|
||||
public byte[] Hash() => Convert.FromBase64String(this.ModelHash);
|
||||
}
|
||||
}
|
231
NoSoliciting/Ml/MlFilter.cs
Normal file
231
NoSoliciting/Ml/MlFilter.cs
Normal file
|
@ -0,0 +1,231 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NoSoliciting.Interface;
|
||||
using NoSoliciting.Resources;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace NoSoliciting.Ml {
|
||||
public class MlFilter : IDisposable {
|
||||
public static string? LastError { get; private set; }
|
||||
|
||||
private const string ManifestName = "manifest.yaml";
|
||||
private const string ModelName = "model.zip";
|
||||
#if DEBUG
|
||||
private const string Url = "http://localhost:8000/manifest.yaml";
|
||||
#else
|
||||
private const string Url = "https://annaclemens.io/assets/nosol/ml/manifest.yaml";
|
||||
#endif
|
||||
|
||||
public uint Version { get; }
|
||||
public Uri ReportUrl { get; }
|
||||
|
||||
private IClassifier Classifier { get; }
|
||||
|
||||
private MlFilter(uint version, Uri reportUrl, IClassifier classifier) {
|
||||
this.Classifier = classifier;
|
||||
this.Version = version;
|
||||
this.ReportUrl = reportUrl;
|
||||
}
|
||||
|
||||
public MessageCategory ClassifyMessage(ushort channel, string message) {
|
||||
var prediction = this.Classifier.Classify(channel, message);
|
||||
var category = MessageCategoryExt.FromString(prediction);
|
||||
|
||||
if (category != null) {
|
||||
return (MessageCategory) category;
|
||||
}
|
||||
|
||||
Plugin.Log.Warning($"Unknown message category: {prediction}");
|
||||
return MessageCategory.Normal;
|
||||
}
|
||||
|
||||
public static async Task<MlFilter?> Load(Plugin plugin, bool showWindow) {
|
||||
plugin.MlStatus = MlFilterStatus.DownloadingManifest;
|
||||
|
||||
// download and parse the remote manifest
|
||||
var manifest = await DownloadManifest();
|
||||
if (manifest == null) {
|
||||
Plugin.Log.Warning("Could not download manifest. Will attempt to fall back on cached version.");
|
||||
}
|
||||
|
||||
// model zip file data
|
||||
byte[]? data = null;
|
||||
|
||||
// load the cached manifest
|
||||
var localManifest = LoadCachedManifest(plugin);
|
||||
// if there is a cached manifest and we either couldn't download/parse the remote OR the cached version is the same as remote version
|
||||
if (localManifest != null && (manifest?.Item1 == null || localManifest.Version == manifest.Value.manifest.Version)) {
|
||||
try {
|
||||
// try to reach the cached model
|
||||
data = await File.ReadAllBytesAsync(CachedFilePath(plugin, ModelName));
|
||||
// set the manifest to our local one and an empty string for the source
|
||||
manifest ??= (localManifest, string.Empty);
|
||||
} catch (IOException) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
// if there is source for the manifest
|
||||
if (!string.IsNullOrEmpty(manifest?.source)) {
|
||||
plugin.MlStatus = MlFilterStatus.DownloadingModel;
|
||||
// download the model if necessary
|
||||
data ??= await DownloadModel(manifest!.Value.manifest!.ModelUrl);
|
||||
}
|
||||
|
||||
// give up if we couldn't get any data at this point
|
||||
if (data == null) {
|
||||
plugin.MlStatus = MlFilterStatus.Uninitialised;
|
||||
return null;
|
||||
}
|
||||
|
||||
// validate checksum
|
||||
var retries = 0;
|
||||
const int maxRetries = 3;
|
||||
|
||||
var correctHash = manifest!.Value.manifest!.Hash();
|
||||
|
||||
using (var sha = SHA256.Create()) {
|
||||
var hash = sha.ComputeHash(data);
|
||||
|
||||
while (!hash.SequenceEqual(correctHash) && retries < maxRetries) {
|
||||
Plugin.Log.Warning($"Model checksum did not match. Redownloading (attempt {retries + 1}/{maxRetries})");
|
||||
retries += 1;
|
||||
|
||||
data = await DownloadModel(manifest!.Value.manifest!.ModelUrl);
|
||||
if (data != null) {
|
||||
hash = sha.ComputeHash(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// give up if we couldn't get any data at this point
|
||||
if (data == null) {
|
||||
plugin.MlStatus = MlFilterStatus.Uninitialised;
|
||||
return null;
|
||||
}
|
||||
|
||||
plugin.MlStatus = MlFilterStatus.Initialising;
|
||||
|
||||
// if there is source for the manifest
|
||||
if (!string.IsNullOrEmpty(manifest!.Value.source)) {
|
||||
// update the cached files
|
||||
UpdateCachedFile(plugin, ModelName, data);
|
||||
UpdateCachedFile(plugin, ManifestName, Encoding.UTF8.GetBytes(manifest.Value.source));
|
||||
}
|
||||
|
||||
// initialise the classifier
|
||||
var classifier = new Classifier();
|
||||
classifier.Initialise(data);
|
||||
|
||||
return new MlFilter(
|
||||
manifest.Value.manifest!.Version,
|
||||
manifest.Value.manifest!.ReportUrl,
|
||||
classifier
|
||||
);
|
||||
}
|
||||
|
||||
private static async Task<byte[]?> DownloadModel(Uri url) {
|
||||
try {
|
||||
using var client = new WebClient();
|
||||
var data = await client.DownloadDataTaskAsync(url);
|
||||
return data;
|
||||
} catch (WebException e) {
|
||||
Plugin.Log.Error("Could not download newest model.");
|
||||
Plugin.Log.Error(e.ToString());
|
||||
LastError = e.Message;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string CachedFilePath(Plugin plugin, string name) {
|
||||
var pluginFolder = plugin.Interface.ConfigDirectory.ToString();
|
||||
Directory.CreateDirectory(pluginFolder);
|
||||
return Path.Combine(pluginFolder, name);
|
||||
}
|
||||
|
||||
private static async void UpdateCachedFile(Plugin plugin, string name, byte[] data) {
|
||||
var cachePath = CachedFilePath(plugin, name);
|
||||
|
||||
var file = File.Create(cachePath);
|
||||
await file.WriteAsync(data, 0, data.Length);
|
||||
await file.FlushAsync();
|
||||
await file.DisposeAsync();
|
||||
}
|
||||
|
||||
private static async Task<(Manifest manifest, string source)?> DownloadManifest() {
|
||||
try {
|
||||
using var client = new WebClient();
|
||||
var data = await client.DownloadStringTaskAsync(Url);
|
||||
LastError = null;
|
||||
return (LoadYaml<Manifest>(data), data);
|
||||
} catch (Exception e) when (e is WebException or YamlException) {
|
||||
Plugin.Log.Error("Could not download newest model manifest.");
|
||||
Plugin.Log.Error(e.ToString());
|
||||
LastError = e.Message;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Manifest? LoadCachedManifest(Plugin plugin) {
|
||||
var manifestPath = CachedFilePath(plugin, ManifestName);
|
||||
if (!File.Exists(manifestPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string data;
|
||||
try {
|
||||
data = File.ReadAllText(manifestPath);
|
||||
} catch (IOException) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return LoadYaml<Manifest>(data);
|
||||
} catch (YamlException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static T LoadYaml<T>(string data) {
|
||||
var de = new DeserializerBuilder()
|
||||
.WithNamingConvention(UnderscoredNamingConvention.Instance)
|
||||
.IgnoreUnmatchedProperties()
|
||||
.Build();
|
||||
return de.Deserialize<T>(data);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Classifier.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public enum MlFilterStatus {
|
||||
Uninitialised,
|
||||
Preparing,
|
||||
DownloadingManifest,
|
||||
DownloadingModel,
|
||||
Initialising,
|
||||
Initialised,
|
||||
}
|
||||
|
||||
public static class MlFilterStatusExt {
|
||||
public static string Description(this MlFilterStatus status) {
|
||||
return status switch {
|
||||
MlFilterStatus.Uninitialised => Language.ModelStatusUninitialised,
|
||||
MlFilterStatus.Preparing => Language.ModelStatusPreparing,
|
||||
MlFilterStatus.DownloadingManifest => Language.ModelStatusDownloadingManifest,
|
||||
MlFilterStatus.DownloadingModel => Language.ModelStatusDownloadingModel,
|
||||
MlFilterStatus.Initialising => Language.ModelStatusInitialising,
|
||||
MlFilterStatus.Initialised => Language.ModelStatusInitialised,
|
||||
_ => status.ToString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
110
NoSoliciting/Ml/Models.cs
Normal file
110
NoSoliciting/Ml/Models.cs
Normal file
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using NoSoliciting.Resources;
|
||||
|
||||
namespace NoSoliciting.Ml {
|
||||
public enum MessageCategory {
|
||||
Trade,
|
||||
FreeCompany,
|
||||
Normal,
|
||||
Phishing,
|
||||
RmtContent,
|
||||
RmtGil,
|
||||
Roleplaying,
|
||||
Static,
|
||||
Community,
|
||||
StaticSub,
|
||||
Fluff,
|
||||
}
|
||||
|
||||
public static class MessageCategoryExt {
|
||||
public static readonly MessageCategory[] UiOrder = {
|
||||
MessageCategory.Trade,
|
||||
MessageCategory.FreeCompany,
|
||||
MessageCategory.Phishing,
|
||||
MessageCategory.RmtContent,
|
||||
MessageCategory.RmtGil,
|
||||
MessageCategory.Roleplaying,
|
||||
MessageCategory.Static,
|
||||
MessageCategory.StaticSub,
|
||||
MessageCategory.Community,
|
||||
MessageCategory.Fluff,
|
||||
};
|
||||
|
||||
public static MessageCategory? FromString(string? category) => category switch {
|
||||
"TRADE" => MessageCategory.Trade,
|
||||
"FC" => MessageCategory.FreeCompany,
|
||||
"NORMAL" => MessageCategory.Normal,
|
||||
"PHISH" => MessageCategory.Phishing,
|
||||
"RMT_C" => MessageCategory.RmtContent,
|
||||
"RMT_G" => MessageCategory.RmtGil,
|
||||
"RP" => MessageCategory.Roleplaying,
|
||||
"STATIC" => MessageCategory.Static,
|
||||
"COMMUNITY" => MessageCategory.Community,
|
||||
"STATIC_SUB" => MessageCategory.StaticSub,
|
||||
"FLUFF" => MessageCategory.Fluff,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
#if DEBUG
|
||||
public static MessageCategory? FromName(string? category) => category switch {
|
||||
"Trade ads" => MessageCategory.Trade,
|
||||
"Free Company ads" => MessageCategory.FreeCompany,
|
||||
"Normal messages" => MessageCategory.Normal,
|
||||
"Phishing messages" => MessageCategory.Phishing,
|
||||
"RMT (content)" => MessageCategory.RmtContent,
|
||||
"RMT (gil)" => MessageCategory.RmtGil,
|
||||
"Roleplaying ads" => MessageCategory.Roleplaying,
|
||||
"Static recruitment" => MessageCategory.Static,
|
||||
"Community ads" => MessageCategory.Community,
|
||||
"Static substitutes" => MessageCategory.StaticSub,
|
||||
"Fluff" => MessageCategory.Fluff,
|
||||
_ => null,
|
||||
};
|
||||
#endif
|
||||
|
||||
public static string ToModelName(this MessageCategory category) => category switch {
|
||||
MessageCategory.Trade => "TRADE",
|
||||
MessageCategory.FreeCompany => "FC",
|
||||
MessageCategory.Normal => "NORMAL",
|
||||
MessageCategory.Phishing => "PHISH",
|
||||
MessageCategory.RmtContent => "RMT_C",
|
||||
MessageCategory.RmtGil => "RMT_G",
|
||||
MessageCategory.Roleplaying => "RP",
|
||||
MessageCategory.Static => "STATIC",
|
||||
MessageCategory.Community => "COMMUNITY",
|
||||
MessageCategory.StaticSub => "STATIC_SUB",
|
||||
MessageCategory.Fluff => "FLUFF",
|
||||
_ => throw new ArgumentException("Invalid category", nameof(category)),
|
||||
};
|
||||
|
||||
public static string Name(this MessageCategory category) => category switch {
|
||||
MessageCategory.Trade => Language.TradeCategory,
|
||||
MessageCategory.FreeCompany => Language.FreeCompanyCategory,
|
||||
MessageCategory.Normal => Language.NormalCategory,
|
||||
MessageCategory.Phishing => Language.PhishingCategory,
|
||||
MessageCategory.RmtContent => Language.RmtContentCategory,
|
||||
MessageCategory.RmtGil => Language.RmtGilCategory,
|
||||
MessageCategory.Roleplaying => Language.RoleplayingCategory,
|
||||
MessageCategory.Static => Language.StaticCategory,
|
||||
MessageCategory.Community => Language.CommunityCategory,
|
||||
MessageCategory.StaticSub => Language.StaticSubCategory,
|
||||
MessageCategory.Fluff => Language.FluffCategory,
|
||||
_ => throw new ArgumentException("Invalid category", nameof(category)),
|
||||
};
|
||||
|
||||
public static string Description(this MessageCategory category) => category switch {
|
||||
MessageCategory.Trade => Language.TradeDescription,
|
||||
MessageCategory.FreeCompany => Language.FreeCompanyDescription,
|
||||
MessageCategory.Normal => Language.NormalDescription,
|
||||
MessageCategory.Phishing => Language.PhishingDescription,
|
||||
MessageCategory.RmtContent => Language.RmtContentDescription,
|
||||
MessageCategory.RmtGil => Language.RmtGilDescription,
|
||||
MessageCategory.Roleplaying => Language.RoleplayingDescription,
|
||||
MessageCategory.Static => Language.StaticDescription,
|
||||
MessageCategory.Community => Language.CommunityDescription,
|
||||
MessageCategory.StaticSub => Language.StaticSubDescription,
|
||||
MessageCategory.Fluff => Language.FluffDescription,
|
||||
_ => throw new ArgumentException("Invalid category", nameof(category)),
|
||||
};
|
||||
}
|
||||
}
|
172
NoSoliciting/NoSoliciting.csproj
Normal file → Executable file
172
NoSoliciting/NoSoliciting.csproj
Normal file → Executable file
|
@ -1,104 +1,74 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Microsoft.CodeAnalysis.FxCopAnalyzers.2.9.6\build\Microsoft.CodeAnalysis.FxCopAnalyzers.props" Condition="Exists('..\packages\Microsoft.CodeAnalysis.FxCopAnalyzers.2.9.6\build\Microsoft.CodeAnalysis.FxCopAnalyzers.props')" />
|
||||
<Import Project="..\packages\Microsoft.NetFramework.Analyzers.2.9.6\build\Microsoft.NetFramework.Analyzers.props" Condition="Exists('..\packages\Microsoft.NetFramework.Analyzers.2.9.6\build\Microsoft.NetFramework.Analyzers.props')" />
|
||||
<Import Project="..\packages\Microsoft.NetCore.Analyzers.2.9.6\build\Microsoft.NetCore.Analyzers.props" Condition="Exists('..\packages\Microsoft.NetCore.Analyzers.2.9.6\build\Microsoft.NetCore.Analyzers.props')" />
|
||||
<Import Project="..\packages\Microsoft.CodeQuality.Analyzers.2.9.6\build\Microsoft.CodeQuality.Analyzers.props" Condition="Exists('..\packages\Microsoft.CodeQuality.Analyzers.2.9.6\build\Microsoft.CodeQuality.Analyzers.props')" />
|
||||
<Import Project="..\packages\Microsoft.CodeAnalysis.VersionCheckAnalyzer.2.9.6\build\Microsoft.CodeAnalysis.VersionCheckAnalyzer.props" Condition="Exists('..\packages\Microsoft.CodeAnalysis.VersionCheckAnalyzer.2.9.6\build\Microsoft.CodeAnalysis.VersionCheckAnalyzer.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{E4C12987-9064-4788-8783-BE418B2C0142}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>NoSoliciting</RootNamespace>
|
||||
<AssemblyName>NoSoliciting</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Dalamud">
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ImGui.NET, Version=1.72.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGui.NET.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ImGuiScene, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGuiScene.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GlobalSuppressions.cs" />
|
||||
<Compile Include="PFPacket.cs" />
|
||||
<Compile Include="Plugin.cs" />
|
||||
<Compile Include="PluginConfiguration.cs" />
|
||||
<Compile Include="PluginUI.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RMTDetection.Chat.cs" />
|
||||
<Compile Include="RMTDetection.cs" />
|
||||
<Compile Include="RMTDetection.PartyFinder.cs" />
|
||||
<Compile Include="RMTUtil.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="NoSoliciting.json" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.VersionCheckAnalyzer.2.9.6\analyzers\dotnet\cs\Microsoft.CodeAnalysis.VersionCheckAnalyzer.resources.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.VersionCheckAnalyzer.2.9.6\analyzers\dotnet\Microsoft.CodeAnalysis.VersionCheckAnalyzer.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.CodeQuality.Analyzers.2.9.6\analyzers\dotnet\cs\Humanizer.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.CodeQuality.Analyzers.2.9.6\analyzers\dotnet\cs\Microsoft.CodeQuality.Analyzers.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.CodeQuality.Analyzers.2.9.6\analyzers\dotnet\cs\Microsoft.CodeQuality.CSharp.Analyzers.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.NetCore.Analyzers.2.9.6\analyzers\dotnet\cs\Microsoft.NetCore.Analyzers.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.NetCore.Analyzers.2.9.6\analyzers\dotnet\cs\Microsoft.NetCore.CSharp.Analyzers.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.NetFramework.Analyzers.2.9.6\analyzers\dotnet\cs\Microsoft.NetFramework.Analyzers.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.NetFramework.Analyzers.2.9.6\analyzers\dotnet\cs\Microsoft.NetFramework.CSharp.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>3.0.8</Version>
|
||||
<TargetFramework>net7-windows</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.CodeAnalysis.VersionCheckAnalyzer.2.9.6\build\Microsoft.CodeAnalysis.VersionCheckAnalyzer.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeAnalysis.VersionCheckAnalyzer.2.9.6\build\Microsoft.CodeAnalysis.VersionCheckAnalyzer.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.CodeQuality.Analyzers.2.9.6\build\Microsoft.CodeQuality.Analyzers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeQuality.Analyzers.2.9.6\build\Microsoft.CodeQuality.Analyzers.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.NetCore.Analyzers.2.9.6\build\Microsoft.NetCore.Analyzers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.NetCore.Analyzers.2.9.6\build\Microsoft.NetCore.Analyzers.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.NetFramework.Analyzers.2.9.6\build\Microsoft.NetFramework.Analyzers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.NetFramework.Analyzers.2.9.6\build\Microsoft.NetFramework.Analyzers.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.CodeAnalysis.FxCopAnalyzers.2.9.6\build\Microsoft.CodeAnalysis.FxCopAnalyzers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeAnalysis.FxCopAnalyzers.2.9.6\build\Microsoft.CodeAnalysis.FxCopAnalyzers.props'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<DalamudLibPath>$(AppData)\XIVLauncher\addon\Hooks\dev</DalamudLibPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
|
||||
<DalamudLibPath>$(DALAMUD_HOME)</DalamudLibPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsCI)' == 'true'">
|
||||
<DalamudLibPath>$(HOME)/dalamud</DalamudLibPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Dalamud">
|
||||
<HintPath>$(DalamudLibPath)\Dalamud.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImGui.NET">
|
||||
<HintPath>$(DalamudLibPath)\ImGui.NET.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Lumina">
|
||||
<HintPath>$(DalamudLibPath)\Lumina.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Lumina.Excel">
|
||||
<HintPath>$(DalamudLibPath)\Lumina.Excel.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>$(DalamudLibPath)\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dalamud.ContextMenu" Version="1.3.1"/>
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.12"/>
|
||||
<PackageReference Include="Fody" Version="6.8.0" PrivateAssets="all"/>
|
||||
<PackageReference Include="Microsoft.ML" Version="2.0.1"/>
|
||||
<PackageReference Include="Resourcer.Fody" Version="1.8.1" PrivateAssets="all"/>
|
||||
<PackageReference Include="XivCommon" Version="9.0.0"/>
|
||||
<PackageReference Include="YamlDotNet" Version="13.4.0"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources\Language.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Language.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NoSoliciting.Interface\NoSoliciting.Interface.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\Language.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>NoSoliciting.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"Author": "ascclemens",
|
||||
"Name": "NoSoliciting",
|
||||
"Description": "Hides RMT in chat and the party finder. /prmt",
|
||||
"InternalName": "NoSoliciting",
|
||||
"AssemblyVersion": "1.1.2",
|
||||
"RepoUrl": "https://sr.ht/~jkcclemens/NoSoliciting",
|
||||
"ApplicableVersion": "any",
|
||||
"DalamudApiLevel": 1
|
||||
}
|
19
NoSoliciting/NoSoliciting.yaml
Normal file
19
NoSoliciting/NoSoliciting.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
author: Anna
|
||||
name: NoSoliciting
|
||||
punchline: Adblock for FFXIV.
|
||||
description: |-
|
||||
Customisable chat and Party Finder filtering. In addition to letting
|
||||
you filter anything from chat and PF, it comes with built-in filters
|
||||
for the following:
|
||||
|
||||
- RMT (both gil and content)
|
||||
- FC ads
|
||||
- RP ads
|
||||
- Phishing messages
|
||||
- Static recruitment
|
||||
- Trade ads
|
||||
- Community ads
|
||||
- Any PF with an item level over the max
|
||||
repo_url: https://git.anna.lgbt/anna/NoSoliciting
|
||||
# Use higher priority to filter messages before other plugins get them.
|
||||
load_priority: 100
|
|
@ -1,94 +0,0 @@
|
|||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public static class PacketInfo {
|
||||
public static readonly int packetSize = Marshal.SizeOf<PFPacket>();
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PFPacket {
|
||||
private readonly int unk0;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
private readonly byte[] padding1;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public PFListing[] listings;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PFListing {
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)]
|
||||
private readonly byte[] header1;
|
||||
|
||||
internal readonly ushort duty;
|
||||
internal readonly ushort dutyType;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
|
||||
private readonly byte[] header2;
|
||||
|
||||
internal readonly ushort world;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
private readonly byte[] header3;
|
||||
|
||||
internal readonly byte objective;
|
||||
internal readonly byte beginnersWelcome;
|
||||
internal readonly byte conditions;
|
||||
internal readonly byte dutyFinderSettings;
|
||||
internal readonly byte lootRules;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
internal readonly byte[] header4; // all zero in every pf I've examined
|
||||
|
||||
internal readonly uint lastPatchHotfixTimestamp; // last time the servers were restarted?
|
||||
internal readonly ushort secondsRemaining;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||
private readonly byte[] header5; // 00 00 01 00 00 00 in every pf I've examined
|
||||
|
||||
internal readonly ushort minimumItemLevel;
|
||||
internal readonly ushort homeWorld;
|
||||
internal readonly ushort currentWorld;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
private readonly byte[] header6; // 02 XX 01 00 in every pf I've examined
|
||||
|
||||
internal readonly byte searchArea;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
|
||||
private readonly byte[] header7; // 00 01 00 00 00 for every pf except alliance raids where it's 01 03 00 00 00 (second byte # parties?)
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
internal readonly uint[] slots;
|
||||
private readonly uint job; // job started as?
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
private readonly byte[] header8; // all zero in every pf I've examined
|
||||
|
||||
// Note that ByValTStr will not work here because the strings are UTF-8 and there's only a CharSet for UTF-16 in C#.
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
|
||||
private readonly byte[] name;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 192)]
|
||||
private readonly byte[] description;
|
||||
|
||||
private static string HandleString(byte[] bytes) {
|
||||
byte[] nonNull = bytes.TakeWhile(b => b != 0).ToArray();
|
||||
return Encoding.UTF8.GetString(nonNull);
|
||||
}
|
||||
|
||||
internal string Name() {
|
||||
return HandleString(this.name);
|
||||
}
|
||||
|
||||
internal string Description() {
|
||||
return HandleString(this.description);
|
||||
}
|
||||
|
||||
internal bool IsNull() {
|
||||
// a valid party finder must have at least one slot set
|
||||
return this.slots.All(slot => slot == 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +1,177 @@
|
|||
using Dalamud.Game.Command;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud;
|
||||
using Dalamud.ContextMenu;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.Plugin.Services;
|
||||
using NoSoliciting.Interface;
|
||||
using NoSoliciting.Ml;
|
||||
using NoSoliciting.Resources;
|
||||
using XivCommon;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public partial class Plugin : IDalamudPlugin {
|
||||
private bool disposedValue;
|
||||
// ReSharper disable once ClassNeverInstantiated.Global
|
||||
public class Plugin : IDalamudPlugin {
|
||||
private bool _disposedValue;
|
||||
|
||||
public string Name => "NoSoliciting";
|
||||
internal static string Name => "NoSoliciting";
|
||||
|
||||
private DalamudPluginInterface pi;
|
||||
private PluginUI ui;
|
||||
private RMTDetection rmt;
|
||||
private Filter Filter { get; }
|
||||
|
||||
public PluginConfiguration Config { get; private set; }
|
||||
[PluginService]
|
||||
internal static IPluginLog Log { get; private set; } = null!;
|
||||
|
||||
public void Initialize(DalamudPluginInterface pluginInterface) {
|
||||
this.pi = pluginInterface ?? throw new ArgumentNullException(nameof(pluginInterface), "DalamudPluginInterface cannot be null");
|
||||
this.ui = new PluginUI(this);
|
||||
[PluginService]
|
||||
internal DalamudPluginInterface Interface { get; init; } = null!;
|
||||
|
||||
this.Config = this.pi.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration();
|
||||
this.Config.Initialise(this.pi);
|
||||
[PluginService]
|
||||
private IClientState ClientState { get; init; } = null!;
|
||||
|
||||
this.rmt = new RMTDetection(this);
|
||||
[PluginService]
|
||||
internal IChatGui ChatGui { get; init; } = null!;
|
||||
|
||||
this.pi.Framework.Network.OnNetworkMessage += this.rmt.OnNetwork;
|
||||
this.pi.Framework.Gui.Chat.OnCheckMessageHandled += this.rmt.OnChat;
|
||||
this.pi.UiBuilder.OnBuildUi += this.ui.Draw;
|
||||
this.pi.UiBuilder.OnOpenConfigUi += this.ui.OpenSettings;
|
||||
this.pi.CommandManager.AddHandler("/prmt", new CommandInfo(OnCommand) {
|
||||
HelpMessage = "Opens the NoSoliciting configuration"
|
||||
});
|
||||
}
|
||||
[PluginService]
|
||||
internal IPartyFinderGui PartyFinderGui { get; init; } = null!;
|
||||
|
||||
public void OnCommand(string command, string args) {
|
||||
this.ui.OpenSettings(null, null);
|
||||
[PluginService]
|
||||
internal IDataManager DataManager { get; init; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal ICommandManager CommandManager { get; init; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal IToastGui ToastGui { get; init; } = null!;
|
||||
|
||||
internal PluginConfiguration Config { get; }
|
||||
internal XivCommonBase Common { get; }
|
||||
internal DalamudContextMenu DalamudContextMenu { get; }
|
||||
internal PluginUi Ui { get; }
|
||||
private Commands Commands { get; }
|
||||
private ContextMenu ContextMenu { get; }
|
||||
internal MlFilterStatus MlStatus { get; set; } = MlFilterStatus.Uninitialised;
|
||||
internal MlFilter? MlFilter { get; set; }
|
||||
|
||||
private readonly List<Message> _messageHistory = new();
|
||||
internal IEnumerable<Message> MessageHistory => this._messageHistory;
|
||||
|
||||
private readonly List<Message> _partyFinderHistory = new();
|
||||
internal IEnumerable<Message> PartyFinderHistory => this._partyFinderHistory;
|
||||
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local
|
||||
public string AssemblyLocation { get; private set; } = Assembly.GetExecutingAssembly().Location;
|
||||
|
||||
public Plugin() {
|
||||
string path = Environment.GetEnvironmentVariable("PATH")!;
|
||||
string newPath = Path.GetDirectoryName(this.AssemblyLocation)!;
|
||||
Environment.SetEnvironmentVariable("PATH", $"{path};{newPath}");
|
||||
|
||||
this.Config = this.Interface.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration();
|
||||
this.Config.Initialise(this.Interface);
|
||||
|
||||
this.ConfigureLanguage();
|
||||
this.Interface.LanguageChanged += this.OnLanguageUpdate;
|
||||
|
||||
this.Common = new XivCommonBase(this.Interface, Hooks.PartyFinderListings);
|
||||
this.DalamudContextMenu = new DalamudContextMenu(this.Interface);
|
||||
|
||||
this.Ui = new PluginUi(this);
|
||||
this.Commands = new Commands(this);
|
||||
this.ContextMenu = new ContextMenu(this);
|
||||
|
||||
this.Filter = new Filter(this);
|
||||
|
||||
this.InitialiseMachineLearning(false);
|
||||
|
||||
// pre-compute the max ilvl to prevent stutter
|
||||
try {
|
||||
FilterUtil.MaxItemLevelAttainable(this.DataManager);
|
||||
} catch (Exception ex) {
|
||||
Plugin.Log.Error(ex, "Exception while computing max item level");
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
if (!this.disposedValue) {
|
||||
if (disposing) {
|
||||
this.pi.Framework.Network.OnNetworkMessage -= this.rmt.OnNetwork;
|
||||
this.pi.Framework.Gui.Chat.OnCheckMessageHandled -= this.rmt.OnChat;
|
||||
this.pi.UiBuilder.OnBuildUi -= this.ui.Draw;
|
||||
this.pi.UiBuilder.OnOpenConfigUi -= this.ui.OpenSettings;
|
||||
this.pi.CommandManager.RemoveHandler("/prmt");
|
||||
}
|
||||
|
||||
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
|
||||
// TODO: set large fields to null
|
||||
disposedValue = true;
|
||||
if (this._disposedValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing) {
|
||||
this.Filter.Dispose();
|
||||
this.MlFilter?.Dispose();
|
||||
this.ContextMenu.Dispose();
|
||||
this.Commands.Dispose();
|
||||
this.Ui.Dispose();
|
||||
this.DalamudContextMenu.Dispose();
|
||||
this.Common.Dispose();
|
||||
this.Interface.LanguageChanged -= this.OnLanguageUpdate;
|
||||
}
|
||||
|
||||
this._disposedValue = true;
|
||||
}
|
||||
|
||||
private void OnLanguageUpdate(string langCode) {
|
||||
this.ConfigureLanguage(langCode);
|
||||
}
|
||||
|
||||
internal void ConfigureLanguage(string? langCode = null) {
|
||||
if (this.Config.FollowGameLanguage) {
|
||||
langCode = this.ClientState.ClientLanguage switch {
|
||||
ClientLanguage.Japanese => "ja",
|
||||
ClientLanguage.English => "en",
|
||||
ClientLanguage.German => "de",
|
||||
ClientLanguage.French => "fr",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(this.ClientState.ClientLanguage), "Unknown ClientLanguage"),
|
||||
};
|
||||
}
|
||||
|
||||
langCode ??= this.Interface.UiLanguage;
|
||||
// I don't fucking trust this. Not since last time.
|
||||
// ReSharper disable once ConstantNullCoalescingCondition
|
||||
Language.Culture = new CultureInfo(langCode ?? "en");
|
||||
}
|
||||
|
||||
internal void InitialiseMachineLearning(bool showWindow) {
|
||||
if (this.MlFilter != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(async () => this.MlFilter = await MlFilter.Load(this, showWindow))
|
||||
.ContinueWith(e => {
|
||||
if (e.IsFaulted) {
|
||||
this.MlStatus = MlFilterStatus.Uninitialised;
|
||||
return;
|
||||
}
|
||||
|
||||
this.MlStatus = MlFilterStatus.Initialised;
|
||||
Log.Info("Machine learning model loaded");
|
||||
});
|
||||
}
|
||||
|
||||
public void AddMessageHistory(Message message) {
|
||||
this._messageHistory.Insert(0, message);
|
||||
|
||||
while (this._messageHistory.Count > 250) {
|
||||
this._messageHistory.RemoveAt(this._messageHistory.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearPartyFinderHistory() {
|
||||
this._partyFinderHistory.Clear();
|
||||
}
|
||||
|
||||
public void AddPartyFinderHistory(Message message) {
|
||||
this._partyFinderHistory.Add(message);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,162 @@
|
|||
using Dalamud.Configuration;
|
||||
using Dalamud.Plugin;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NoSoliciting.Ml;
|
||||
|
||||
namespace NoSoliciting {
|
||||
[Serializable]
|
||||
public class PluginConfiguration : IPluginConfiguration {
|
||||
[NonSerialized]
|
||||
private DalamudPluginInterface pi;
|
||||
public static readonly PluginConfiguration Default = new();
|
||||
|
||||
public int Version { get; set; } = 1;
|
||||
private DalamudPluginInterface Interface { get; set; } = null!;
|
||||
|
||||
public bool FilterChat { get; set; } = true;
|
||||
public bool FilterPartyFinder { get; set; } = true;
|
||||
public int Version { get; set; } = 2;
|
||||
|
||||
public bool AdvancedMode { get; set; } = false;
|
||||
public bool AdvancedMode { get; set; }
|
||||
|
||||
public bool CustomChatFilter { get; set; } = false;
|
||||
public List<string> ChatSubstrings { get; } = new List<string>();
|
||||
public List<string> ChatRegexes { get; } = new List<string>();
|
||||
public bool CustomChatFilter { get; set; }
|
||||
public List<string> ChatSubstrings { get; } = new();
|
||||
public List<string> ChatRegexes { get; } = new();
|
||||
|
||||
public bool CustomPFFilter { get; set; } = false;
|
||||
public List<string> PFSubstrings { get; } = new List<string>();
|
||||
public List<string> PFRegexes { get; } = new List<string>();
|
||||
[JsonIgnore]
|
||||
public List<Regex> CompiledChatRegexes { get; private set; } = new();
|
||||
|
||||
public bool CustomPFFilter { get; set; }
|
||||
public List<string> PFSubstrings { get; } = new();
|
||||
public List<string> PFRegexes { get; } = new();
|
||||
|
||||
[JsonIgnore]
|
||||
public List<Regex> CompiledPfRegexes { get; private set; } = new();
|
||||
|
||||
public bool FilterHugeItemLevelPFs { get; set; }
|
||||
|
||||
public bool FollowGameLanguage { get; set; }
|
||||
|
||||
public HashSet<MessageCategory> BasicMlFilters { get; set; } = new() {
|
||||
MessageCategory.RmtGil,
|
||||
MessageCategory.RmtContent,
|
||||
MessageCategory.Phishing,
|
||||
};
|
||||
|
||||
public Dictionary<MessageCategory, HashSet<ChatType>> MlFilters { get; set; } = new() {
|
||||
[MessageCategory.RmtGil] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
ChatType.Say,
|
||||
ChatType.Shout,
|
||||
},
|
||||
[MessageCategory.RmtContent] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
ChatType.Say,
|
||||
ChatType.Shout,
|
||||
},
|
||||
[MessageCategory.Phishing] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
ChatType.TellIncoming,
|
||||
},
|
||||
[MessageCategory.Roleplaying] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
ChatType.Shout,
|
||||
ChatType.Yell,
|
||||
},
|
||||
[MessageCategory.FreeCompany] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
ChatType.Shout,
|
||||
ChatType.Yell,
|
||||
ChatType.TellIncoming,
|
||||
},
|
||||
[MessageCategory.Static] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
},
|
||||
[MessageCategory.StaticSub] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
},
|
||||
[MessageCategory.Trade] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
ChatType.Shout,
|
||||
ChatType.Yell,
|
||||
},
|
||||
[MessageCategory.Community] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
ChatType.Shout,
|
||||
ChatType.Yell,
|
||||
},
|
||||
[MessageCategory.Fluff] = new HashSet<ChatType> {
|
||||
ChatType.None,
|
||||
ChatType.Shout,
|
||||
ChatType.Yell,
|
||||
},
|
||||
};
|
||||
|
||||
public bool LogFilteredPfs { get; set; } = true;
|
||||
public bool LogFilteredChat { get; set; } = true;
|
||||
|
||||
public bool ConsiderPrivatePfs { get; set; }
|
||||
|
||||
public IEnumerable<string> ValidChatSubstrings => this.ChatSubstrings.Where(needle => !string.IsNullOrWhiteSpace(needle));
|
||||
public IEnumerable<string> ValidPfSubstrings => this.PFSubstrings.Where(needle => !string.IsNullOrWhiteSpace(needle));
|
||||
|
||||
public void Initialise(DalamudPluginInterface pi) {
|
||||
this.pi = pi ?? throw new ArgumentNullException(nameof(pi), "DalamudPluginInterface cannot be null");
|
||||
this.Interface = pi;
|
||||
this.CompileRegexes();
|
||||
}
|
||||
|
||||
public void Save() {
|
||||
this.pi.SavePluginConfig(this);
|
||||
this.Interface.SavePluginConfig(this);
|
||||
}
|
||||
|
||||
public void CompileRegexes() {
|
||||
this.CompiledChatRegexes = this.ChatRegexes
|
||||
.Where(reg => !string.IsNullOrWhiteSpace(reg))
|
||||
.Select(reg => new Regex(reg, RegexOptions.Compiled))
|
||||
.ToList();
|
||||
this.CompiledPfRegexes = this.PFRegexes
|
||||
.Where(reg => !string.IsNullOrWhiteSpace(reg))
|
||||
.Select(reg => new Regex(reg, RegexOptions.Compiled))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
internal bool MlEnabledOn(MessageCategory category, ChatType chatType) {
|
||||
HashSet<ChatType>? filtered;
|
||||
|
||||
if (this.AdvancedMode) {
|
||||
if (!this.MlFilters.TryGetValue(category, out filtered)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// check to see if the user has this category filtered
|
||||
if (!this.BasicMlFilters.Contains(category)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the chat types that this category is enabled on by default
|
||||
if (!Default.MlFilters.TryGetValue(category, out filtered)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return filtered.Contains(chatType);
|
||||
}
|
||||
|
||||
internal IEnumerable<MessageCategory> CreateFiltersClone() {
|
||||
var filters = new HashSet<MessageCategory>();
|
||||
|
||||
foreach (var category in (MessageCategory[]) Enum.GetValues(typeof(MessageCategory))) {
|
||||
if (this.AdvancedMode) {
|
||||
if (this.MlFilters.TryGetValue(category, out var filtered) && filtered.Count > 0) {
|
||||
filters.Add(category);
|
||||
}
|
||||
} else {
|
||||
if (this.BasicMlFilters.Contains(category)) {
|
||||
filters.Add(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,178 +0,0 @@
|
|||
using ImGuiNET;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public class PluginUI {
|
||||
private readonly Plugin plugin;
|
||||
|
||||
private bool _showSettings;
|
||||
public bool ShowSettings { get => this._showSettings; set => this._showSettings = value; }
|
||||
|
||||
public PluginUI(Plugin plugin) {
|
||||
this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
|
||||
}
|
||||
|
||||
public void OpenSettings(object sender, EventArgs e) {
|
||||
this.ShowSettings = true;
|
||||
}
|
||||
|
||||
public void Draw() {
|
||||
if (this.ShowSettings) {
|
||||
this.DrawSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSettings() {
|
||||
if (ImGui.Begin($"{this.plugin.Name} settings", ref this._showSettings)) {
|
||||
if (this.plugin.Config.AdvancedMode) {
|
||||
this.DrawAdvancedSettings();
|
||||
} else {
|
||||
this.DrawBasicSettings();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
bool advanced = this.plugin.Config.AdvancedMode;
|
||||
if (ImGui.Checkbox("Advanced mode", ref advanced)) {
|
||||
this.plugin.Config.AdvancedMode = advanced;
|
||||
this.plugin.Config.Save();
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBasicSettings() {
|
||||
ImGui.SetWindowSize(new Vector2(225, 125));
|
||||
|
||||
bool filterChat = this.plugin.Config.FilterChat;
|
||||
if (ImGui.Checkbox("Filter RMT from chat", ref filterChat)) {
|
||||
this.plugin.Config.FilterChat = filterChat;
|
||||
this.plugin.Config.Save();
|
||||
}
|
||||
|
||||
bool filterPartyFinder = this.plugin.Config.FilterPartyFinder;
|
||||
if (ImGui.Checkbox("Filter RMT from Party Finder", ref filterPartyFinder)) {
|
||||
this.plugin.Config.FilterPartyFinder = filterPartyFinder;
|
||||
this.plugin.Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawAdvancedSettings() {
|
||||
ImGui.SetWindowSize(new Vector2(600, 400));
|
||||
|
||||
if (ImGui.BeginTabBar("##nosoliciting-tabs")) {
|
||||
if (ImGui.BeginTabItem("Chat")) {
|
||||
bool filterChat = this.plugin.Config.FilterChat;
|
||||
if (ImGui.Checkbox("Enable built-in RMT filter", ref filterChat)) {
|
||||
this.plugin.Config.FilterChat = filterChat;
|
||||
this.plugin.Config.Save();
|
||||
}
|
||||
|
||||
bool customChat = this.plugin.Config.CustomChatFilter;
|
||||
if (ImGui.Checkbox("Enable custom chat filters", ref customChat)) {
|
||||
this.plugin.Config.CustomChatFilter = customChat;
|
||||
this.plugin.Config.Save();
|
||||
}
|
||||
|
||||
if (this.plugin.Config.CustomChatFilter) {
|
||||
List<string> substrings = this.plugin.Config.ChatSubstrings;
|
||||
List<string> regexes = this.plugin.Config.ChatRegexes;
|
||||
this.DrawCustom("chat", ref substrings, ref regexes);
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Party Finder")) {
|
||||
bool filterPartyFinder = this.plugin.Config.FilterPartyFinder;
|
||||
if (ImGui.Checkbox("Enable built-in Party Finder RMT filter", ref filterPartyFinder)) {
|
||||
this.plugin.Config.FilterPartyFinder = filterPartyFinder;
|
||||
this.plugin.Config.Save();
|
||||
}
|
||||
|
||||
bool customPF = this.plugin.Config.CustomPFFilter;
|
||||
if (ImGui.Checkbox("Enable custom Party Finder filters", ref customPF)) {
|
||||
this.plugin.Config.CustomPFFilter = customPF;
|
||||
this.plugin.Config.Save();
|
||||
}
|
||||
|
||||
if (this.plugin.Config.CustomPFFilter) {
|
||||
List<string> substrings = this.plugin.Config.PFSubstrings;
|
||||
List<string> regexes = this.plugin.Config.PFRegexes;
|
||||
this.DrawCustom("pf", ref substrings, ref regexes);
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
ImGui.EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCustom(string name, ref List<string> substrings, ref List<string> regexes) {
|
||||
ImGui.Columns(2);
|
||||
|
||||
ImGui.Text("Substrings to filter");
|
||||
if (ImGui.BeginChild($"##{name}-substrings", new Vector2(0, 175))) {
|
||||
for (int i = 0; i < substrings.Count; i++) {
|
||||
string input = substrings[i];
|
||||
if (ImGui.InputText($"##{name}-substring-{i}", ref input, 100)) {
|
||||
if (input.Length != 0) {
|
||||
substrings[i] = input;
|
||||
}
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button($"Remove##{name}-substring-{i}-remove")) {
|
||||
substrings.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.Button($"Add##{name}-substring-add")) {
|
||||
substrings.Add("");
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
ImGui.NextColumn();
|
||||
|
||||
ImGui.Text("Regular expressions to filter");
|
||||
if (ImGui.BeginChild($"##{name}-regexes", new Vector2(0, 175))) {
|
||||
for (int i = 0; i < regexes.Count; i++) {
|
||||
string input = regexes[i];
|
||||
if (ImGui.InputText($"##{name}-regex-{i}", ref input, 100)) {
|
||||
bool valid = true;
|
||||
try {
|
||||
_ = new Regex(input);
|
||||
} catch (ArgumentException) {
|
||||
valid = false;
|
||||
}
|
||||
if (valid && input.Length != 0) {
|
||||
regexes[i] = input;
|
||||
}
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button($"Remove##{name}-regex-{i}-remove")) {
|
||||
regexes.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.Button($"Add##{name}-regex-add")) {
|
||||
regexes.Add("");
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
ImGui.Columns(1);
|
||||
|
||||
if (ImGui.Button($"Save filters##{name}-save")) {
|
||||
this.plugin.Config.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
using System.Resources;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("NoSoliciting")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("NoSoliciting")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("e4c12987-9064-4788-8783-be418b2c0142")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.1.2")]
|
||||
[assembly: AssemblyFileVersion("1.1.2")]
|
||||
[assembly: NeutralResourcesLanguage("en-GB")]
|
|
@ -1,65 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public partial class RMTDetection {
|
||||
public static class Chat {
|
||||
private static readonly string[] rmtSubstrings = {
|
||||
"4KGOLD",
|
||||
"We have sufficient stock",
|
||||
"PVPBANK.COM",
|
||||
"Gil for free",
|
||||
"www.so9.com",
|
||||
"Fast & Convenient",
|
||||
"Cheap & Safety Guarantee",
|
||||
"【Code|A O A U E",
|
||||
"igfans",
|
||||
"4KGOLD.COM",
|
||||
"Cheapest Gil with",
|
||||
"pvp and bank on google",
|
||||
"Selling Cheap GIL",
|
||||
"ff14mogstation.com",
|
||||
"Cheap Gil 1000k",
|
||||
"gilsforyou",
|
||||
"server 1000K =",
|
||||
"gils_selling",
|
||||
"E A S Y.C O M",
|
||||
"bonus code",
|
||||
"mins delivery guarantee",
|
||||
"Sell cheap",
|
||||
"Salegm.com",
|
||||
"cheap Mog",
|
||||
"Off Code:",
|
||||
"FF14Mog.com",
|
||||
"使用する5%オ",
|
||||
"offers Fantasia",
|
||||
};
|
||||
private static readonly Regex[] rmtRegexes = {
|
||||
new Regex(@"Off Code( *)", RegexOptions.Compiled),
|
||||
};
|
||||
|
||||
public static bool IsRMT(string msg) {
|
||||
msg = RMTUtil.Normalise(msg);
|
||||
|
||||
return rmtSubstrings.Any(needle => msg.Contains(needle))
|
||||
|| rmtRegexes.Any(needle => needle.IsMatch(msg));
|
||||
}
|
||||
|
||||
public static bool MatchesCustomFilters(string msg, PluginConfiguration config) {
|
||||
if (config == null) {
|
||||
throw new ArgumentNullException(nameof(config), "PluginConfiguration cannot be null");
|
||||
}
|
||||
|
||||
if (!config.AdvancedMode || !config.CustomChatFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
msg = RMTUtil.Normalise(msg);
|
||||
|
||||
return config.ChatSubstrings.Any(needle => msg.Contains(needle))
|
||||
|| config.ChatRegexes.Any(needle => Regex.IsMatch(msg, needle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public partial class RMTDetection {
|
||||
public static class PartyFinder {
|
||||
private static readonly Regex[] discord = {
|
||||
new Regex(@".#\d{4}", RegexOptions.Compiled),
|
||||
new Regex(@"https://discord\.(gg|io)/\w+", RegexOptions.Compiled),
|
||||
};
|
||||
private static readonly string[] content = {
|
||||
"eden",
|
||||
"savage",
|
||||
"primal",
|
||||
"ultimate",
|
||||
};
|
||||
private static readonly string[] selling = {
|
||||
"sell",
|
||||
"$ell",
|
||||
"sale",
|
||||
"price",
|
||||
"cheap",
|
||||
};
|
||||
|
||||
public static bool IsRMT(string desc) {
|
||||
if (desc == null) {
|
||||
throw new ArgumentNullException(nameof(desc), "description string cannot be null");
|
||||
}
|
||||
|
||||
desc = RMTUtil.Normalise(desc).ToLowerInvariant();
|
||||
|
||||
bool containsSell = selling.Any(needle => desc.Contains(needle));
|
||||
bool containsContent = content.Any(needle => desc.Contains(needle));
|
||||
bool containsDiscord = discord.Any(needle => needle.IsMatch(desc));
|
||||
|
||||
return containsSell && containsDiscord && containsContent;
|
||||
}
|
||||
|
||||
public static bool MatchesCustomFilters(string msg, PluginConfiguration config) {
|
||||
if (config == null) {
|
||||
throw new ArgumentNullException(nameof(config), "PluginConfiguration cannot be null");
|
||||
}
|
||||
|
||||
if (!config.AdvancedMode || !config.CustomPFFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
msg = RMTUtil.Normalise(msg);
|
||||
|
||||
return config.PFSubstrings.Any(needle => msg.Contains(needle))
|
||||
|| config.PFRegexes.Any(needle => Regex.IsMatch(msg, needle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
using Dalamud.Game.Chat;
|
||||
using Dalamud.Game.Chat.SeStringHandling;
|
||||
using Dalamud.Game.Internal.Network;
|
||||
using Dalamud.Plugin;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public partial class RMTDetection {
|
||||
private const ushort PF_LISTING = 0x358;
|
||||
//private static ushort PF_SUMMARY = 0x223;
|
||||
|
||||
private readonly Plugin plugin;
|
||||
|
||||
public RMTDetection(Plugin plugin) {
|
||||
this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "fulfilling a delegate")]
|
||||
public void OnNetwork(IntPtr dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction) {
|
||||
// only filter when enabled
|
||||
if (!this.plugin.Config.FilterPartyFinder) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only look at packets coming in
|
||||
if (direction != NetworkMessageDirection.ZoneDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// PF_LISTING is sent repeatedly until PF_SUMMARY, which is a summary (and also the packet sent for the chat notifs)
|
||||
if (opCode != PF_LISTING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the packet into a struct
|
||||
PFPacket packet = Marshal.PtrToStructure<PFPacket>(dataPtr);
|
||||
|
||||
for (int i = 0; i < packet.listings.Length; i++) {
|
||||
PFListing listing = packet.listings[i];
|
||||
|
||||
// only look at listings that aren't null
|
||||
if (listing.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string desc = listing.Description();
|
||||
|
||||
// only look at listings that are RMT
|
||||
if (!PartyFinder.IsRMT(desc) && !PartyFinder.MatchesCustomFilters(desc, this.plugin.Config)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// replace the listing with an empty one
|
||||
packet.listings[i] = new PFListing();
|
||||
|
||||
PluginLog.Log($"Filtered out PF listing from {listing.Name()}: {listing.Description()}");
|
||||
}
|
||||
|
||||
// get some memory for writing to
|
||||
byte[] newPacket = new byte[PacketInfo.packetSize];
|
||||
GCHandle pinnedArray = GCHandle.Alloc(newPacket, GCHandleType.Pinned);
|
||||
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
|
||||
|
||||
// write our struct into the memory (doing this directly crashes the game)
|
||||
Marshal.StructureToPtr(packet, pointer, false);
|
||||
|
||||
// copy our new memory over the game's
|
||||
Marshal.Copy(newPacket, 0, dataPtr, PacketInfo.packetSize);
|
||||
|
||||
// free memory
|
||||
pinnedArray.Free();
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "fulfilling a delegate")]
|
||||
public void OnChat(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) {
|
||||
if (message == null) {
|
||||
throw new ArgumentNullException(nameof(message), "SeString cannot be null");
|
||||
}
|
||||
|
||||
// only filter when enabled
|
||||
if (!this.plugin.Config.FilterChat) {
|
||||
return;
|
||||
}
|
||||
|
||||
string text = message.TextValue;
|
||||
|
||||
if (!Chat.IsRMT(text) && !Chat.MatchesCustomFilters(text, this.plugin.Config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PluginLog.Log($"Handled RMT message: {text}");
|
||||
isHandled = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NoSoliciting {
|
||||
public static class RMTUtil {
|
||||
private static readonly Dictionary<char, string> replacements = new Dictionary<char, string>() {
|
||||
// alphabet
|
||||
['\ue070'] = "?",
|
||||
['\ue071'] = "A",
|
||||
['\ue072'] = "B",
|
||||
['\ue073'] = "C",
|
||||
['\ue074'] = "D",
|
||||
['\ue075'] = "E",
|
||||
['\ue076'] = "F",
|
||||
['\ue077'] = "G",
|
||||
['\ue078'] = "H",
|
||||
['\ue079'] = "I",
|
||||
['\ue07a'] = "J",
|
||||
['\ue07b'] = "K",
|
||||
['\ue07c'] = "L",
|
||||
['\ue07d'] = "M",
|
||||
['\ue07e'] = "N",
|
||||
['\ue07f'] = "O",
|
||||
['\ue080'] = "P",
|
||||
['\ue081'] = "Q",
|
||||
['\ue082'] = "R",
|
||||
['\ue083'] = "S",
|
||||
['\ue084'] = "T",
|
||||
['\ue085'] = "U",
|
||||
['\ue086'] = "V",
|
||||
['\ue087'] = "W",
|
||||
['\ue088'] = "X",
|
||||
['\ue089'] = "Y",
|
||||
['\ue08a'] = "Z",
|
||||
|
||||
// letters in other sets
|
||||
['\ue022'] = "A",
|
||||
['\ue024'] = "_A",
|
||||
['\ue0b0'] = "E",
|
||||
};
|
||||
|
||||
public static string Normalise(string input) {
|
||||
if (input == null) {
|
||||
throw new ArgumentNullException(nameof(input), "input cannot be null");
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<char, string> entry in replacements) {
|
||||
input = input.Replace($"{entry.Key}", entry.Value);
|
||||
}
|
||||
return input.Normalize(NormalizationForm.FormKD);
|
||||
}
|
||||
}
|
||||
}
|
855
NoSoliciting/Resources/Language.Designer.cs
generated
Executable file
855
NoSoliciting/Resources/Language.Designer.cs
generated
Executable file
|
@ -0,0 +1,855 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace NoSoliciting.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Language {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Language() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NoSoliciting.Resources.Language", typeof(Language).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Advanced mode.
|
||||
/// </summary>
|
||||
internal static string AdvancedMode {
|
||||
get {
|
||||
return ResourceManager.GetString("AdvancedMode", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Do not change advanced settings unless you know what you are doing..
|
||||
/// </summary>
|
||||
internal static string AdvancedWarning1 {
|
||||
get {
|
||||
return ResourceManager.GetString("AdvancedWarning1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The machine learning model was trained with certain channels in mind..
|
||||
/// </summary>
|
||||
internal static string AdvancedWarning2 {
|
||||
get {
|
||||
return ResourceManager.GetString("AdvancedWarning2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Chat filters.
|
||||
/// </summary>
|
||||
internal static string ChatFilters {
|
||||
get {
|
||||
return ResourceManager.GetString("ChatFilters", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Community ads.
|
||||
/// </summary>
|
||||
internal static string CommunityCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("CommunityCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Advertisements for general-purpose communities, generally Discord servers.
|
||||
/// </summary>
|
||||
internal static string CommunityDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("CommunityDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enable custom chat filters.
|
||||
/// </summary>
|
||||
internal static string EnableCustomChatFilters {
|
||||
get {
|
||||
return ResourceManager.GetString("EnableCustomChatFilters", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enable custom Party Finder filters.
|
||||
/// </summary>
|
||||
internal static string EnableCustomPartyFinderFilters {
|
||||
get {
|
||||
return ResourceManager.GetString("EnableCustomPartyFinderFilters", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Filter PFs with item level above maximum.
|
||||
/// </summary>
|
||||
internal static string FilterIlvlPfs {
|
||||
get {
|
||||
return ResourceManager.GetString("FilterIlvlPfs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Apply filters to private Party Finder listings.
|
||||
/// </summary>
|
||||
internal static string FilterPrivatePfs {
|
||||
get {
|
||||
return ResourceManager.GetString("FilterPrivatePfs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Filters.
|
||||
/// </summary>
|
||||
internal static string FiltersTab {
|
||||
get {
|
||||
return ResourceManager.GetString("FiltersTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fluff.
|
||||
/// </summary>
|
||||
internal static string FluffCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("FluffCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Messages that don't fall into other categories and are not for content (Party Finder).
|
||||
/// </summary>
|
||||
internal static string FluffDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("FluffDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Free Company ads.
|
||||
/// </summary>
|
||||
internal static string FreeCompanyCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("FreeCompanyCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Advertisements for Free Companies.
|
||||
/// </summary>
|
||||
internal static string FreeCompanyDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("FreeCompanyDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log filtered messages.
|
||||
/// </summary>
|
||||
internal static string LogFilteredMessages {
|
||||
get {
|
||||
return ResourceManager.GetString("LogFilteredMessages", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log filtered PFs.
|
||||
/// </summary>
|
||||
internal static string LogFilteredPfs {
|
||||
get {
|
||||
return ResourceManager.GetString("LogFilteredPfs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Downloading model manifest.
|
||||
/// </summary>
|
||||
internal static string ModelStatusDownloadingManifest {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelStatusDownloadingManifest", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Downloading model.
|
||||
/// </summary>
|
||||
internal static string ModelStatusDownloadingModel {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelStatusDownloadingModel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Initialised.
|
||||
/// </summary>
|
||||
internal static string ModelStatusInitialised {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelStatusInitialised", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Initialising model and classifier.
|
||||
/// </summary>
|
||||
internal static string ModelStatusInitialising {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelStatusInitialising", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Preparing to update model.
|
||||
/// </summary>
|
||||
internal static string ModelStatusPreparing {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelStatusPreparing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Uninitialised.
|
||||
/// </summary>
|
||||
internal static string ModelStatusUninitialised {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelStatusUninitialised", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Model.
|
||||
/// </summary>
|
||||
internal static string ModelTab {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Last error: {0}.
|
||||
/// </summary>
|
||||
internal static string ModelTabError {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelTabError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Model status: {0}.
|
||||
/// </summary>
|
||||
internal static string ModelTabStatus {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelTabStatus", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Version: {0}.
|
||||
/// </summary>
|
||||
internal static string ModelTabVersion {
|
||||
get {
|
||||
return ResourceManager.GetString("ModelTabVersion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Normal messages.
|
||||
/// </summary>
|
||||
internal static string NormalCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("NormalCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Normal messages that should not be filtered.
|
||||
/// </summary>
|
||||
internal static string NormalDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("NormalDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Other filters.
|
||||
/// </summary>
|
||||
internal static string OtherFiltersTab {
|
||||
get {
|
||||
return ResourceManager.GetString("OtherFiltersTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use the game's language setting instead of Dalamud's.
|
||||
/// </summary>
|
||||
internal static string OtherGameLanguage {
|
||||
get {
|
||||
return ResourceManager.GetString("OtherGameLanguage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Other.
|
||||
/// </summary>
|
||||
internal static string OtherTab {
|
||||
get {
|
||||
return ResourceManager.GetString("OtherTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Party Finder filters.
|
||||
/// </summary>
|
||||
internal static string PartyFinderFilters {
|
||||
get {
|
||||
return ResourceManager.GetString("PartyFinderFilters", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Phishing messages.
|
||||
/// </summary>
|
||||
internal static string PhishingCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("PhishingCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Messages trying to trick you into revealing your account details in order to steal your account.
|
||||
/// </summary>
|
||||
internal static string PhishingDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("PhishingDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Regular expressions to filter.
|
||||
/// </summary>
|
||||
internal static string RegularExpressionsToFilter {
|
||||
get {
|
||||
return ResourceManager.GetString("RegularExpressionsToFilter", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Chat.
|
||||
/// </summary>
|
||||
internal static string ReportChatTab {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportChatTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Channel.
|
||||
/// </summary>
|
||||
internal static string ReportColumnChannel {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportColumnChannel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Description.
|
||||
/// </summary>
|
||||
internal static string ReportColumnDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportColumnDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Host.
|
||||
/// </summary>
|
||||
internal static string ReportColumnHost {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportColumnHost", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Message.
|
||||
/// </summary>
|
||||
internal static string ReportColumnMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportColumnMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reason.
|
||||
/// </summary>
|
||||
internal static string ReportColumnReason {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportColumnReason", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sender.
|
||||
/// </summary>
|
||||
internal static string ReportColumnSender {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportColumnSender", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Timestamp.
|
||||
/// </summary>
|
||||
internal static string ReportColumnTimestamp {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportColumnTimestamp", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Click on one of the entries below to report it to the developer as miscategorised..
|
||||
/// </summary>
|
||||
internal static string ReportHelp {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportHelp", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} reporting.
|
||||
/// </summary>
|
||||
internal static string Reporting {
|
||||
get {
|
||||
return ResourceManager.GetString("Reporting", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel.
|
||||
/// </summary>
|
||||
internal static string ReportModalCancel {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalCancel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Other.
|
||||
/// </summary>
|
||||
internal static string ReportModalClassificationOther {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalClassificationOther", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Messages that don't fall under any of the other categories..
|
||||
/// </summary>
|
||||
internal static string ReportModalClassificationOtherDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalClassificationOtherDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copy to clipboard.
|
||||
/// </summary>
|
||||
internal static string ReportModalCopy {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalCopy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reporting is disabled because your ML model was not functioning when you saw this message..
|
||||
/// </summary>
|
||||
internal static string ReportModalDisabledBadModel {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalDisabledBadModel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You cannot report messages filtered because of a custom filter..
|
||||
/// </summary>
|
||||
internal static string ReportModalDisabledCustom {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalDisabledCustom", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reporting is disabled because you weren't filtering for this kind of message at the time you saw it..
|
||||
/// </summary>
|
||||
internal static string ReportModalDisabledFilterNotEnabled {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalDisabledFilterNotEnabled", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You cannot report messages filtered because of item level..
|
||||
/// </summary>
|
||||
internal static string ReportModalDisabledItemLevel {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalDisabledItemLevel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reporting is disabled because you must choose a different classification than the original..
|
||||
/// </summary>
|
||||
internal static string ReportModalDisabledSameClassification {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalDisabledSameClassification", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Go to custom filters.
|
||||
/// </summary>
|
||||
internal static string ReportModalGoToCustomButton {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalGoToCustomButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reporting this message will let the developer know that you think this message was incorrectly classified..
|
||||
/// </summary>
|
||||
internal static string ReportModalHelp1 {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalHelp1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to NoSoliciting only works for English messages. Do not report non-English messages..
|
||||
/// </summary>
|
||||
internal static string ReportModalHelp2 {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalHelp2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This message's original classification: {0}.
|
||||
/// </summary>
|
||||
internal static string ReportModalOriginalClassification {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalOriginalClassification", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Report.
|
||||
/// </summary>
|
||||
internal static string ReportModalReport {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalReport", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to How do you think this message should have been classified?.
|
||||
/// </summary>
|
||||
internal static string ReportModalSuggestedClassification {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalSuggestedClassification", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Report to {0}.
|
||||
/// </summary>
|
||||
internal static string ReportModalTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportModalTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Party Finder.
|
||||
/// </summary>
|
||||
internal static string ReportPartyFinderTab {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportPartyFinderTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to failed to send.
|
||||
/// </summary>
|
||||
internal static string ReportStatusFailure {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportStatusFailure", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to sending.
|
||||
/// </summary>
|
||||
internal static string ReportStatusInProgress {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportStatusInProgress", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Last report status: {0}.
|
||||
/// </summary>
|
||||
internal static string ReportStatusMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportStatusMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to sent successfully.
|
||||
/// </summary>
|
||||
internal static string ReportStatusSuccessful {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportStatusSuccessful", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to unknown.
|
||||
/// </summary>
|
||||
internal static string ReportStatusUnknown {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportStatusUnknown", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Failed to report Party Finder listing hosted by {0}..
|
||||
/// </summary>
|
||||
internal static string ReportToastFailure {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportToastFailure", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Party Finder listing hosted by {0} reported successfully..
|
||||
/// </summary>
|
||||
internal static string ReportToastSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportToastSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Report to NoSoliciting.
|
||||
/// </summary>
|
||||
internal static string ReportToNoSoliciting {
|
||||
get {
|
||||
return ResourceManager.GetString("ReportToNoSoliciting", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to RMT (content).
|
||||
/// </summary>
|
||||
internal static string RmtContentCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("RmtContentCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Real-money trade involving content (also known as content sellers).
|
||||
/// </summary>
|
||||
internal static string RmtContentDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("RmtContentDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to RMT (gil).
|
||||
/// </summary>
|
||||
internal static string RmtGilCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("RmtGilCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Real-money trade involving gil or items (also known as RMT bots).
|
||||
/// </summary>
|
||||
internal static string RmtGilDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("RmtGilDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Roleplaying ads.
|
||||
/// </summary>
|
||||
internal static string RoleplayingCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("RoleplayingCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Advertisements for personal RP, RP communities, venues, or anything else related to roleplaying.
|
||||
/// </summary>
|
||||
internal static string RoleplayingDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("RoleplayingDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Save filters.
|
||||
/// </summary>
|
||||
internal static string SaveFilters {
|
||||
get {
|
||||
return ResourceManager.GetString("SaveFilters", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} settings.
|
||||
/// </summary>
|
||||
internal static string Settings {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show reporting window.
|
||||
/// </summary>
|
||||
internal static string ShowReportingWindow {
|
||||
get {
|
||||
return ResourceManager.GetString("ShowReportingWindow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Static recruitment.
|
||||
/// </summary>
|
||||
internal static string StaticCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("StaticCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Statics looking for members or players looking for a static.
|
||||
/// </summary>
|
||||
internal static string StaticDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("StaticDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Static substitutes.
|
||||
/// </summary>
|
||||
internal static string StaticSubCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("StaticSubCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Statics looking for fill-ins of missing members for clears.
|
||||
/// </summary>
|
||||
internal static string StaticSubDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("StaticSubDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Substrings to filter.
|
||||
/// </summary>
|
||||
internal static string SubstringsToFilter {
|
||||
get {
|
||||
return ResourceManager.GetString("SubstringsToFilter", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Trade ads.
|
||||
/// </summary>
|
||||
internal static string TradeCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("TradeCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Messages advertising trading items or services for gil, such as omnicrafters looking for work or people selling rare items off the market board.
|
||||
/// </summary>
|
||||
internal static string TradeDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("TradeDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Update model.
|
||||
/// </summary>
|
||||
internal static string UpdateModel {
|
||||
get {
|
||||
return ResourceManager.GetString("UpdateModel", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
264
NoSoliciting/Resources/Language.de.resx
Normal file
264
NoSoliciting/Resources/Language.de.resx
Normal file
|
@ -0,0 +1,264 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ReportToNoSoliciting" xml:space="preserve">
|
||||
<value>An NoSoliciting melden</value>
|
||||
</data>
|
||||
<data name="OtherTab" xml:space="preserve">
|
||||
<value>Anderes</value>
|
||||
</data>
|
||||
<data name="OtherFiltersTab" xml:space="preserve">
|
||||
<value>Andere Filter</value>
|
||||
</data>
|
||||
<data name="ChatFilters" xml:space="preserve">
|
||||
<value>Chat Filter</value>
|
||||
</data>
|
||||
<data name="PartyFinderFilters" xml:space="preserve">
|
||||
<value>Gruppensuche Filter</value>
|
||||
</data>
|
||||
<data name="FilterIlvlPfs" xml:space="preserve">
|
||||
<value>Filtere Gruppensuchen, dessen Gegenstandsstufe über dem Maximum liegt</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Einstellungen</value>
|
||||
</data>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>Benutzerdefinierte Chat Filter aktivieren</value>
|
||||
</data>
|
||||
<data name="AdvancedMode" xml:space="preserve">
|
||||
<value>Erweiterte Einstellungen</value>
|
||||
</data>
|
||||
<data name="ModelTabError" xml:space="preserve">
|
||||
<value>Letzter Fehler: {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabVersion" xml:space="preserve">
|
||||
<value>Version: {0}</value>
|
||||
</data>
|
||||
<data name="FiltersTab" xml:space="preserve">
|
||||
<value>Filter</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>Filter auf private Gruppensuchen anwenden</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>Benutzerdefinierte Gruppensuche Filter aktivieren</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>zu filternde Zeichenketten</value>
|
||||
</data>
|
||||
<data name="UpdateModel" xml:space="preserve">
|
||||
<value>Modell aktualisieren</value>
|
||||
</data>
|
||||
<data name="ModelTab" xml:space="preserve">
|
||||
<value>Modell</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning2" xml:space="preserve">
|
||||
<value>Das Machine Learning Modell wurde in Hinblick auf spezielle Kanäle trainiert.</value>
|
||||
</data>
|
||||
<data name="LogFilteredPfs" xml:space="preserve">
|
||||
<value>Gefilterte Gruppensuchen protokollieren</value>
|
||||
</data>
|
||||
<data name="ReportStatusSuccessful" xml:space="preserve">
|
||||
<value>erfolgreich gesendet</value>
|
||||
</data>
|
||||
<data name="ReportStatusInProgress" xml:space="preserve">
|
||||
<value>sende</value>
|
||||
</data>
|
||||
<data name="ReportStatusUnknown" xml:space="preserve">
|
||||
<value>unbekannt</value>
|
||||
</data>
|
||||
<data name="ReportChatTab" xml:space="preserve">
|
||||
<value>Chat</value>
|
||||
</data>
|
||||
<data name="ReportColumnTimestamp" xml:space="preserve">
|
||||
<value>Zeitstempel</value>
|
||||
</data>
|
||||
<data name="SaveFilters" xml:space="preserve">
|
||||
<value>Filter speichern</value>
|
||||
</data>
|
||||
<data name="ReportColumnReason" xml:space="preserve">
|
||||
<value>Grund</value>
|
||||
</data>
|
||||
<data name="ReportColumnMessage" xml:space="preserve">
|
||||
<value>Nachricht</value>
|
||||
</data>
|
||||
<data name="ReportPartyFinderTab" xml:space="preserve">
|
||||
<value>Gruppensuche</value>
|
||||
</data>
|
||||
<data name="ReportStatusMessage" xml:space="preserve">
|
||||
<value>Status der letzten Meldung: {0}</value>
|
||||
</data>
|
||||
<data name="ReportColumnDescription" xml:space="preserve">
|
||||
<value>Beschreibung</value>
|
||||
</data>
|
||||
<data name="ReportModalTitle" xml:space="preserve">
|
||||
<value>An {0} melden</value>
|
||||
</data>
|
||||
<data name="ReportModalCopy" xml:space="preserve">
|
||||
<value>In die Zwischenablage kopieren</value>
|
||||
</data>
|
||||
<data name="FreeCompanyCategory" xml:space="preserve">
|
||||
<value>Werbung für Freie Gesellschaften</value>
|
||||
</data>
|
||||
<data name="ReportModalCancel" xml:space="preserve">
|
||||
<value>Abbrechen</value>
|
||||
</data>
|
||||
<data name="RmtContentCategory" xml:space="preserve">
|
||||
<value>RMT (Inhalte)</value>
|
||||
</data>
|
||||
<data name="RmtGilCategory" xml:space="preserve">
|
||||
<value>RMT (Gil)</value>
|
||||
</data>
|
||||
<data name="ReportStatusFailure" xml:space="preserve">
|
||||
<value>senden fehlgeschlagen</value>
|
||||
</data>
|
||||
<data name="ReportColumnChannel" xml:space="preserve">
|
||||
<value>Kanal</value>
|
||||
</data>
|
||||
<data name="ReportColumnSender" xml:space="preserve">
|
||||
<value>Absender</value>
|
||||
</data>
|
||||
<data name="ReportModalReport" xml:space="preserve">
|
||||
<value>Melden</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledCustom" xml:space="preserve">
|
||||
<value>Du kannst Nachrichten, die aufgrund von benutzerdefinierten Filtern gefiltert wurden, nicht melden.</value>
|
||||
</data>
|
||||
<data name="PhishingCategory" xml:space="preserve">
|
||||
<value>Phishing Nachrichten</value>
|
||||
</data>
|
||||
<data name="NormalCategory" xml:space="preserve">
|
||||
<value>Normale Nachrichten</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp2" xml:space="preserve">
|
||||
<value>NoSoliciting funktioniert nur für englische Nachrichten, bitte ausschließlich englische Nachrichten melden.</value>
|
||||
</data>
|
||||
<data name="RoleplayingCategory" xml:space="preserve">
|
||||
<value>Werbung für Rollenspiel</value>
|
||||
</data>
|
||||
<data name="FreeCompanyDescription" xml:space="preserve">
|
||||
<value>Werbung für Freie Gesellschaften</value>
|
||||
</data>
|
||||
<data name="OtherGameLanguage" xml:space="preserve">
|
||||
<value>Nutze die Spracheinstellung des Spiels, anstelle der von Dalamud</value>
|
||||
</data>
|
||||
<data name="ReportToastFailure" xml:space="preserve">
|
||||
<value>Fehler beim Melden der Gruppensuche von {0}.</value>
|
||||
</data>
|
||||
<data name="ReportToastSuccess" xml:space="preserve">
|
||||
<value>Gruppensuche von {0] erfolgreich gemeldet.</value>
|
||||
</data>
|
||||
<data name="CommunityDescription" xml:space="preserve">
|
||||
<value>Werbung für alle möglichen Gemeinschaften, normalerweise Discord Server</value>
|
||||
</data>
|
||||
<data name="TradeDescription" xml:space="preserve">
|
||||
<value>Werbung für Tauschgeschäfte oder Dienste für Gil, Omnicrafter, die nach Arbeit suchen oder Leute, die seltene Items abseits des Marktbretts verkaufen</value>
|
||||
</data>
|
||||
<data name="PhishingDescription" xml:space="preserve">
|
||||
<value>Nachrichten, die dich dazu bringen sollen deine Account-Daten preiszugeben, um deinen Account zu stehlen</value>
|
||||
</data>
|
||||
<data name="StaticDescription" xml:space="preserve">
|
||||
<value>Statics, die nach neuen Mitgliedern suchen oder Spieler, die nach einer Static suchen</value>
|
||||
</data>
|
||||
<data name="StaticCategory" xml:space="preserve">
|
||||
<value>Static Rekrutierung</value>
|
||||
</data>
|
||||
<data name="StaticSubDescription" xml:space="preserve">
|
||||
<value>Statics, die wegen abwesender Mitglieder nach Aushilfen suchen</value>
|
||||
</data>
|
||||
<data name="StaticSubCategory" xml:space="preserve">
|
||||
<value>Static Aushilfen</value>
|
||||
</data>
|
||||
<data name="NormalDescription" xml:space="preserve">
|
||||
<value>Normale Nachrichten, die nicht gefiltert werden sollten</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning1" xml:space="preserve">
|
||||
<value>Ändere diese Einstellungen nur, wenn du weißt, was du tust.</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>zu filternde reguläre Ausdrücke</value>
|
||||
</data>
|
||||
<data name="ReportHelp" xml:space="preserve">
|
||||
<value>Klicke auf einen der unten stehenden Einträge, um ihn dem Entwickler als falsch kategorisiert zu melden.</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp1" xml:space="preserve">
|
||||
<value>Diese Nachricht zu melden, lässt den Entwickler wissen, dass du denkst, das diese Nachricht falsch kategorisiert wurde.</value>
|
||||
</data>
|
||||
<data name="RoleplayingDescription" xml:space="preserve">
|
||||
<value>Werbung für persönliches Rollenspiel, Rollenspiel Gemeinschaften, Veranstaltungen oder anderes in Bezug auf Rollenspiel</value>
|
||||
</data>
|
||||
<data name="ModelTabStatus" xml:space="preserve">
|
||||
<value>Status des Modells: {0}</value>
|
||||
</data>
|
||||
<data name="LogFilteredMessages" xml:space="preserve">
|
||||
<value>Gefilterte Nachrichten protokollieren</value>
|
||||
</data>
|
||||
<data name="ShowReportingWindow" xml:space="preserve">
|
||||
<value>Berichtsfenster öffnen</value>
|
||||
</data>
|
||||
<data name="ReportColumnHost" xml:space="preserve">
|
||||
<value>Suchender</value>
|
||||
</data>
|
||||
<data name="Reporting" xml:space="preserve">
|
||||
<value>{0} Bericht</value>
|
||||
</data>
|
||||
<data name="RmtContentDescription" xml:space="preserve">
|
||||
<value>Echtgeldhandel, der den Abschluss von Inhalten beinhaltet (auch bekannt als Content Seller)</value>
|
||||
</data>
|
||||
<data name="RmtGilDescription" xml:space="preserve">
|
||||
<value>Echtgeldhandel, der Gil oder Gegenstände beinhaltet (auch bekannt als RMT Bots)</value>
|
||||
</data>
|
||||
<data name="CommunityCategory" xml:space="preserve">
|
||||
<value>Gemeinschaftswerbung</value>
|
||||
</data>
|
||||
<data name="TradeCategory" xml:space="preserve">
|
||||
<value>Handelswerbung</value>
|
||||
</data>
|
||||
<data name="ModelStatusUninitialised" xml:space="preserve">
|
||||
<value>nicht initialisiert</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialised" xml:space="preserve">
|
||||
<value>initialisiert</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingModel" xml:space="preserve">
|
||||
<value>lade Modell herunter</value>
|
||||
</data>
|
||||
<data name="ModelStatusPreparing" xml:space="preserve">
|
||||
<value>bereite Aktualisierung des Modells vor</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingManifest" xml:space="preserve">
|
||||
<value>lade Machine Learning Manifest herunter</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialising" xml:space="preserve">
|
||||
<value>initialisiere Modell und Klassifikator</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledBadModel" xml:space="preserve">
|
||||
<value>Du kannst diese Nachricht nicht melden, weil das Machine Learning Modell nicht funktionierte, als du sie gesehen hast.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledItemLevel" xml:space="preserve">
|
||||
<value>Du kannst Nachrichten, die wegen zu hoher Gegenstandsstufe gefiltert wurden, nicht melden.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledSameClassification" xml:space="preserve">
|
||||
<value>Wähle eine andere Kategorie, bevor du diese Nachricht meldest.</value>
|
||||
</data>
|
||||
<data name="ReportModalOriginalClassification" xml:space="preserve">
|
||||
<value>Ursprüngliche Kategorie dieser Nachricht: {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalSuggestedClassification" xml:space="preserve">
|
||||
<value>Wie denkst du, hätte diese Nachricht kategorisiert werden sollen?</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledFilterNotEnabled" xml:space="preserve">
|
||||
<value>Du kannst diese Nachricht nicht melden, weil du diese Art von Nachrichten nicht gefiltert hast, als du sie gesehen hast.</value>
|
||||
</data>
|
||||
</root>
|
319
NoSoliciting/Resources/Language.es.resx
Normal file
319
NoSoliciting/Resources/Language.es.resx
Normal file
|
@ -0,0 +1,319 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="LogFilteredPfs" xml:space="preserve">
|
||||
<value>Registrar PFs filtrados</value>
|
||||
</data>
|
||||
<data name="LogFilteredMessages" xml:space="preserve">
|
||||
<value>Registrar mensajes filtrados</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Ajustes de {0}</value>
|
||||
</data>
|
||||
<data name="AdvancedMode" xml:space="preserve">
|
||||
<value>Modo avanzado</value>
|
||||
</data>
|
||||
<data name="ModelTab" xml:space="preserve">
|
||||
<value>Modelo</value>
|
||||
</data>
|
||||
<data name="ModelTabVersion" xml:space="preserve">
|
||||
<value>Versión: {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabStatus" xml:space="preserve">
|
||||
<value>Estado del modelo: {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabError" xml:space="preserve">
|
||||
<value>Último error: {0}</value>
|
||||
</data>
|
||||
<data name="FiltersTab" xml:space="preserve">
|
||||
<value>Filtros</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning1" xml:space="preserve">
|
||||
<value>No modifiques los ajustes avanzados salvo que sepas lo que haces.</value>
|
||||
</data>
|
||||
<data name="OtherFiltersTab" xml:space="preserve">
|
||||
<value>Otros filtros</value>
|
||||
</data>
|
||||
<data name="ChatFilters" xml:space="preserve">
|
||||
<value>Filtros de chat</value>
|
||||
</data>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>Habilitar filtros de chat personalizados</value>
|
||||
</data>
|
||||
<data name="PartyFinderFilters" xml:space="preserve">
|
||||
<value>Filtros del Party Finder</value>
|
||||
</data>
|
||||
<data name="FilterIlvlPfs" xml:space="preserve">
|
||||
<value>Filtrar PFs con el item level por encima del máximo</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>Aplicar filtros a listados privados del Party Finder</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>Subcadenas a filtrar</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>Expresiones regulares a filtrar</value>
|
||||
</data>
|
||||
<data name="SaveFilters" xml:space="preserve">
|
||||
<value>Guardar filtros</value>
|
||||
</data>
|
||||
<data name="Reporting" xml:space="preserve">
|
||||
<value>Reportes de {0}</value>
|
||||
</data>
|
||||
<data name="ReportHelp" xml:space="preserve">
|
||||
<value>Haz click en una de las entradas de abajo para informar de que ha sido categorizada erróneamente.</value>
|
||||
</data>
|
||||
<data name="ReportStatusFailure" xml:space="preserve">
|
||||
<value>No se ha podido enviar</value>
|
||||
</data>
|
||||
<data name="ReportStatusSuccessful" xml:space="preserve">
|
||||
<value>Enviado con éxito</value>
|
||||
</data>
|
||||
<data name="ReportStatusInProgress" xml:space="preserve">
|
||||
<value>enviando</value>
|
||||
</data>
|
||||
<data name="ReportStatusUnknown" xml:space="preserve">
|
||||
<value>desconocido</value>
|
||||
</data>
|
||||
<data name="ReportChatTab" xml:space="preserve">
|
||||
<value>Chat</value>
|
||||
</data>
|
||||
<data name="ReportColumnTimestamp" xml:space="preserve">
|
||||
<value>Marca de tiempo</value>
|
||||
</data>
|
||||
<data name="ReportColumnChannel" xml:space="preserve">
|
||||
<value>Canal</value>
|
||||
</data>
|
||||
<data name="ReportColumnReason" xml:space="preserve">
|
||||
<value>Razón</value>
|
||||
</data>
|
||||
<data name="ReportColumnSender" xml:space="preserve">
|
||||
<value>Remitente</value>
|
||||
</data>
|
||||
<data name="ReportColumnMessage" xml:space="preserve">
|
||||
<value>Mensaje</value>
|
||||
</data>
|
||||
<data name="ReportPartyFinderTab" xml:space="preserve">
|
||||
<value>Party Finder</value>
|
||||
</data>
|
||||
<data name="ReportColumnHost" xml:space="preserve">
|
||||
<value>Anfitrión</value>
|
||||
</data>
|
||||
<data name="ReportColumnDescription" xml:space="preserve">
|
||||
<value>Descripción</value>
|
||||
</data>
|
||||
<data name="ReportModalTitle" xml:space="preserve">
|
||||
<value>Informar a {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp1" xml:space="preserve">
|
||||
<value>Al informar de este mensaje le harás saber a la desarrolladora que crees que este mensaje ha sido clasificado de manera errónea.</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp2" xml:space="preserve">
|
||||
<value>NoSoliciting sólo funciona con mensajes en inglés. No informes de mensajes en otros idiomas.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledCustom" xml:space="preserve">
|
||||
<value>No puedes informar de mensajes que han sido filtrados debido a un filtro personalizado.</value>
|
||||
</data>
|
||||
<data name="ReportModalReport" xml:space="preserve">
|
||||
<value>Reportar</value>
|
||||
</data>
|
||||
<data name="ReportModalCopy" xml:space="preserve">
|
||||
<value>Copiar al portapapeles</value>
|
||||
</data>
|
||||
<data name="ReportModalCancel" xml:space="preserve">
|
||||
<value>Cancelar</value>
|
||||
</data>
|
||||
<data name="TradeCategory" xml:space="preserve">
|
||||
<value>Anuncios de intercambio</value>
|
||||
</data>
|
||||
<data name="FreeCompanyCategory" xml:space="preserve">
|
||||
<value>Anuncios de Free Company</value>
|
||||
</data>
|
||||
<data name="PhishingCategory" xml:space="preserve">
|
||||
<value>Mensajes de phishing</value>
|
||||
</data>
|
||||
<data name="RmtContentCategory" xml:space="preserve">
|
||||
<value>RMT (contenido)</value>
|
||||
</data>
|
||||
<data name="RmtGilCategory" xml:space="preserve">
|
||||
<value>RMT (guil)</value>
|
||||
</data>
|
||||
<data name="RoleplayingCategory" xml:space="preserve">
|
||||
<value>Anuncios de RP</value>
|
||||
</data>
|
||||
<data name="StaticCategory" xml:space="preserve">
|
||||
<value>Reclutamiento de statics</value>
|
||||
</data>
|
||||
<data name="CommunityCategory" xml:space="preserve">
|
||||
<value>Anuncios de comunidades</value>
|
||||
</data>
|
||||
<data name="FreeCompanyDescription" xml:space="preserve">
|
||||
<value>Anuncios de Free Companies</value>
|
||||
</data>
|
||||
<data name="NormalDescription" xml:space="preserve">
|
||||
<value>Mensajes normales que no deberían ser filtrados</value>
|
||||
</data>
|
||||
<data name="PhishingDescription" xml:space="preserve">
|
||||
<value>Mensajes intentando engañarte para que reveles detalles de tu cuenta con el objetivo de robártela</value>
|
||||
</data>
|
||||
<data name="RmtContentDescription" xml:space="preserve">
|
||||
<value>Intercambio con dinero real relacionado con contenido (también conocidos como vendedores de contenido)</value>
|
||||
</data>
|
||||
<data name="RoleplayingDescription" xml:space="preserve">
|
||||
<value>Anuncios de RP personal, comunidades de rol, establecimientos donde rolear, o cualquier otra cosa relacionada con el RP</value>
|
||||
</data>
|
||||
<data name="StaticDescription" xml:space="preserve">
|
||||
<value>Statics buscando miembros o jugadores buscando static</value>
|
||||
</data>
|
||||
<data name="CommunityDescription" xml:space="preserve">
|
||||
<value>Anuncios de comunidades de uso general, normalmente servidores de Discord</value>
|
||||
</data>
|
||||
<data name="ReportToNoSoliciting" xml:space="preserve">
|
||||
<value>Informar a NoSoliciting</value>
|
||||
</data>
|
||||
<data name="ReportToastSuccess" xml:space="preserve">
|
||||
<value>El listado de Party Finder de {0} ha sido reportado con éxito.</value>
|
||||
</data>
|
||||
<data name="ReportToastFailure" xml:space="preserve">
|
||||
<value>No se ha podido reportar el listado de Party Finder de {0}.</value>
|
||||
</data>
|
||||
<data name="OtherGameLanguage" xml:space="preserve">
|
||||
<value>Usar el idioma del juego en lugar del idioma en el que está Dalamud</value>
|
||||
</data>
|
||||
<data name="OtherTab" xml:space="preserve">
|
||||
<value>Otros</value>
|
||||
</data>
|
||||
<data name="ShowReportingWindow" xml:space="preserve">
|
||||
<value>Mostrar ventana de reportes</value>
|
||||
</data>
|
||||
<data name="UpdateModel" xml:space="preserve">
|
||||
<value>Actualizar modelo</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning2" xml:space="preserve">
|
||||
<value>El modelo de machine learning ha sido entrenado con ciertos canales en mente.</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>Habilitar filtros del Party Finder personalizados</value>
|
||||
</data>
|
||||
<data name="ReportStatusMessage" xml:space="preserve">
|
||||
<value>Estado del último reporte: {0}</value>
|
||||
</data>
|
||||
<data name="NormalCategory" xml:space="preserve">
|
||||
<value>Mensajes normales</value>
|
||||
</data>
|
||||
<data name="StaticSubCategory" xml:space="preserve">
|
||||
<value>Sustitutos de statics</value>
|
||||
</data>
|
||||
<data name="TradeDescription" xml:space="preserve">
|
||||
<value>Mensajes que anuncian el intercambio de objetos o servicios a cambio de guiles, como crafters buscando trabajo o gente vendiendo objetos poco comunes</value>
|
||||
</data>
|
||||
<data name="RmtGilDescription" xml:space="preserve">
|
||||
<value>Intercambio de dinero real relacionado con guiles u objetos (también conocidos como bots de RMT o vendeoros)</value>
|
||||
</data>
|
||||
<data name="StaticSubDescription" xml:space="preserve">
|
||||
<value>Statics buscando sustitutos temporales para clears</value>
|
||||
</data>
|
||||
<data name="ModelStatusUninitialised" xml:space="preserve">
|
||||
<value>Sin inicializar</value>
|
||||
</data>
|
||||
<data name="ModelStatusPreparing" xml:space="preserve">
|
||||
<value>Preparando actualización del modelo</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingManifest" xml:space="preserve">
|
||||
<value>Descargando manifiesto del modelo</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingModel" xml:space="preserve">
|
||||
<value>Descargando modelo</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialising" xml:space="preserve">
|
||||
<value>Inicializando modelo y clasificador</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialised" xml:space="preserve">
|
||||
<value>Inicializado</value>
|
||||
</data>
|
||||
<data name="ReportModalSuggestedClassification" xml:space="preserve">
|
||||
<value>¿Cómo crees que se debería haber clasificado este mensaje?</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledBadModel" xml:space="preserve">
|
||||
<value>No puedes reportar este mensaje porque tu modelo ML no estaba funcionando correctamente cuando viste este mensaje.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledSameClassification" xml:space="preserve">
|
||||
<value>No puedes reportar este mensaje porque debes elegir una clasificación distinta a la original.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledItemLevel" xml:space="preserve">
|
||||
<value>No puedes reportar mensajes filtrados por item level.</value>
|
||||
</data>
|
||||
<data name="ReportModalOriginalClassification" xml:space="preserve">
|
||||
<value>La clasificación original de este mensaje: {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledFilterNotEnabled" xml:space="preserve">
|
||||
<value>No puedes reportar este mensaje porque no estabas filtrando este tipo de mensaje cuando lo viste.</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOther" xml:space="preserve">
|
||||
<value>Otro</value>
|
||||
</data>
|
||||
<data name="ReportModalGoToCustomButton" xml:space="preserve">
|
||||
<value>Ir a filtros personalizados</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOtherDescription" xml:space="preserve">
|
||||
<value>Mensajes que no encajan en ninguna otra categoría.</value>
|
||||
</data>
|
||||
</root>
|
273
NoSoliciting/Resources/Language.fr.resx
Normal file
273
NoSoliciting/Resources/Language.fr.resx
Normal file
|
@ -0,0 +1,273 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ReportToNoSoliciting" xml:space="preserve">
|
||||
<value>Signaler à NoSoliciting</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Paramètres de {0}</value>
|
||||
</data>
|
||||
<data name="AdvancedMode" xml:space="preserve">
|
||||
<value>Paramètres avancés</value>
|
||||
</data>
|
||||
<data name="SaveFilters" xml:space="preserve">
|
||||
<value>Sauvegarder les filtres</value>
|
||||
</data>
|
||||
<data name="Reporting" xml:space="preserve">
|
||||
<value>Signalement à {0}</value>
|
||||
</data>
|
||||
<data name="ReportHelp" xml:space="preserve">
|
||||
<value>Cliquez sur l'une des entrées pour la signaler à la développeuse comme étant mal catégorisée.</value>
|
||||
</data>
|
||||
<data name="ReportStatusFailure" xml:space="preserve">
|
||||
<value>échec de l'envoi</value>
|
||||
</data>
|
||||
<data name="ReportStatusSuccessful" xml:space="preserve">
|
||||
<value>effectué avec succès</value>
|
||||
</data>
|
||||
<data name="ReportStatusInProgress" xml:space="preserve">
|
||||
<value>envoi en cours</value>
|
||||
</data>
|
||||
<data name="ReportStatusUnknown" xml:space="preserve">
|
||||
<value>inconnu</value>
|
||||
</data>
|
||||
<data name="ReportStatusMessage" xml:space="preserve">
|
||||
<value>État du dernier signalement : {0}</value>
|
||||
</data>
|
||||
<data name="ReportChatTab" xml:space="preserve">
|
||||
<value>Discussion</value>
|
||||
</data>
|
||||
<data name="ReportColumnTimestamp" xml:space="preserve">
|
||||
<value>Date et heure</value>
|
||||
</data>
|
||||
<data name="ReportColumnChannel" xml:space="preserve">
|
||||
<value>Canal</value>
|
||||
</data>
|
||||
<data name="ReportColumnReason" xml:space="preserve">
|
||||
<value>Raison</value>
|
||||
</data>
|
||||
<data name="OtherTab" xml:space="preserve">
|
||||
<value>Autres</value>
|
||||
</data>
|
||||
<data name="ModelTab" xml:space="preserve">
|
||||
<value>Modèle</value>
|
||||
</data>
|
||||
<data name="ModelTabVersion" xml:space="preserve">
|
||||
<value>Version : {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabStatus" xml:space="preserve">
|
||||
<value>État du modèle : {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabError" xml:space="preserve">
|
||||
<value>Dernière erreur rencontrée : {0}</value>
|
||||
</data>
|
||||
<data name="UpdateModel" xml:space="preserve">
|
||||
<value>Mettre à jour le modèle</value>
|
||||
</data>
|
||||
<data name="FiltersTab" xml:space="preserve">
|
||||
<value>Filtres</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning1" xml:space="preserve">
|
||||
<value>Veuillez ne modifier les paramètres avancés que si vous savez exactement ce que vous faites.</value>
|
||||
</data>
|
||||
<data name="LogFilteredPfs" xml:space="preserve">
|
||||
<value>Enregistrer les Recherches d'Équipe (PFs) filtrées</value>
|
||||
</data>
|
||||
<data name="LogFilteredMessages" xml:space="preserve">
|
||||
<value>Enregistrer les messages filtrées</value>
|
||||
</data>
|
||||
<data name="ShowReportingWindow" xml:space="preserve">
|
||||
<value>Ouvrir la fenêtre de signalement</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning2" xml:space="preserve">
|
||||
<value>Le modèle d'apprentissage automatique a été entraîné selon une configuration précise de channels.</value>
|
||||
</data>
|
||||
<data name="OtherFiltersTab" xml:space="preserve">
|
||||
<value>Autres filtres</value>
|
||||
</data>
|
||||
<data name="ChatFilters" xml:space="preserve">
|
||||
<value>Filtres de discussion</value>
|
||||
</data>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>Activer les filtres de discussion personnalisés</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>Appliquer les filtres aux Recherches d'Équipe (PFs) listées en privées</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>Activer les filtres de Recherche d'Équipe (PFs) personnalisés</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>Sous-chaînes (substrings) à filtrer</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>Expressions régulières à filtrer</value>
|
||||
</data>
|
||||
<data name="PartyFinderFilters" xml:space="preserve">
|
||||
<value>Filtres de Recherche d'Équipe (PFs)</value>
|
||||
</data>
|
||||
<data name="FilterIlvlPfs" xml:space="preserve">
|
||||
<value>Filtrer les Recherche d'Équipe (PFs) avec un niveau d'objet requis supérieur au maximum</value>
|
||||
</data>
|
||||
<data name="OtherGameLanguage" xml:space="preserve">
|
||||
<value>Utiliser la langue des paramètres du jeu au lieu de ceux de Dalamud</value>
|
||||
</data>
|
||||
<data name="TradeCategory" xml:space="preserve">
|
||||
<value>Publicités à but d'échange</value>
|
||||
</data>
|
||||
<data name="FreeCompanyCategory" xml:space="preserve">
|
||||
<value>Publicités de compagnie libre</value>
|
||||
</data>
|
||||
<data name="TradeDescription" xml:space="preserve">
|
||||
<value>Messages faisant la publicité d'échanges d'objets ou de services contre des gils, tels que les omnicrafteurs à la recherche de travail ou de vendeurs d'objets rares via le tableau des marchés</value>
|
||||
</data>
|
||||
<data name="PhishingDescription" xml:space="preserve">
|
||||
<value>Messages tentant de vous piéger dans le but de vous faire révéler les détails et identifiants de votre compte afin de vous le voler</value>
|
||||
</data>
|
||||
<data name="RmtGilDescription" xml:space="preserve">
|
||||
<value>Ventes contre de l'argent réel impliquant des gils (aussi connus sous le nom de gils RMT : Real Money Traders)</value>
|
||||
</data>
|
||||
<data name="RmtContentCategory" xml:space="preserve">
|
||||
<value>RMT (contenu)</value>
|
||||
</data>
|
||||
<data name="PhishingCategory" xml:space="preserve">
|
||||
<value>Messages de phishing</value>
|
||||
</data>
|
||||
<data name="NormalCategory" xml:space="preserve">
|
||||
<value>Messages normaux</value>
|
||||
</data>
|
||||
<data name="RmtGilCategory" xml:space="preserve">
|
||||
<value>RMT (gils)</value>
|
||||
</data>
|
||||
<data name="RoleplayingCategory" xml:space="preserve">
|
||||
<value>Publicités pour du jeu de rôle (RP)</value>
|
||||
</data>
|
||||
<data name="StaticCategory" xml:space="preserve">
|
||||
<value>Recrutement de statique</value>
|
||||
</data>
|
||||
<data name="CommunityCategory" xml:space="preserve">
|
||||
<value>Publicités communautaires</value>
|
||||
</data>
|
||||
<data name="StaticSubCategory" xml:space="preserve">
|
||||
<value>Remplacements de statique</value>
|
||||
</data>
|
||||
<data name="FreeCompanyDescription" xml:space="preserve">
|
||||
<value>Messages de recrutement de la part de Compagnies Libres</value>
|
||||
</data>
|
||||
<data name="NormalDescription" xml:space="preserve">
|
||||
<value>Les messages normaux ne devraient pas être filtrés</value>
|
||||
</data>
|
||||
<data name="RmtContentDescription" xml:space="preserve">
|
||||
<value>Ventes contre de l'argent réel impliquant la complétion de contenu (aussi connus sous le nom de content RMT : Real Money Traders)</value>
|
||||
</data>
|
||||
<data name="RoleplayingDescription" xml:space="preserve">
|
||||
<value>Promotion pour du jeu de rôle personnel, des communautés de jeu de rôle, des événements et tout ce qui a trait au jeu de rôle</value>
|
||||
</data>
|
||||
<data name="StaticDescription" xml:space="preserve">
|
||||
<value>Membres d'une statique à la recherche de membres, ou joueurs à la recherche de personnes qui cherchent une statique</value>
|
||||
</data>
|
||||
<data name="ReportToastSuccess" xml:space="preserve">
|
||||
<value>Recherche d'équipe organisée par {0} signalée avec succès.</value>
|
||||
</data>
|
||||
<data name="ReportToastFailure" xml:space="preserve">
|
||||
<value>Échec du signalement de la Recherche d'Équipe de {0].</value>
|
||||
</data>
|
||||
<data name="ReportColumnSender" xml:space="preserve">
|
||||
<value>Expéditeur</value>
|
||||
</data>
|
||||
<data name="ReportColumnMessage" xml:space="preserve">
|
||||
<value>Message</value>
|
||||
</data>
|
||||
<data name="ReportPartyFinderTab" xml:space="preserve">
|
||||
<value>Recherche d'Équipe</value>
|
||||
</data>
|
||||
<data name="ReportColumnHost" xml:space="preserve">
|
||||
<value>Recruteur</value>
|
||||
</data>
|
||||
<data name="ReportColumnDescription" xml:space="preserve">
|
||||
<value>Description</value>
|
||||
</data>
|
||||
<data name="ReportModalTitle" xml:space="preserve">
|
||||
<value>Signaler à {0}</value>
|
||||
</data>
|
||||
<data name="CommunityDescription" xml:space="preserve">
|
||||
<value>Publicités pour des communautés de tous types et pour tous propos, généralement des serveurs Discord</value>
|
||||
</data>
|
||||
<data name="StaticSubDescription" xml:space="preserve">
|
||||
<value>Statique à la recherche de remplaçants de leurs membres manquants pour leur complétion</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp1" xml:space="preserve">
|
||||
<value>Signaler ce message avertira la développeuse que vous pensez qu'il a été incorrectement classifié.</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp2" xml:space="preserve">
|
||||
<value>NoSoliciting n'est fonctionnel que pour les messages en anglais. Veuillez ne pas signaler des messages qui ne sont pas en anglais.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledCustom" xml:space="preserve">
|
||||
<value>Vous ne pouvez pas signaler de messages filtrés à cause d'un filtre personnalisé.</value>
|
||||
</data>
|
||||
<data name="ReportModalReport" xml:space="preserve">
|
||||
<value>Signaler</value>
|
||||
</data>
|
||||
<data name="ReportModalCopy" xml:space="preserve">
|
||||
<value>Copier dans le presse-papiers</value>
|
||||
</data>
|
||||
<data name="ReportModalCancel" xml:space="preserve">
|
||||
<value>Annuler</value>
|
||||
</data>
|
||||
<data name="ModelStatusUninitialised" xml:space="preserve">
|
||||
<value>Non-initialisée</value>
|
||||
</data>
|
||||
<data name="ModelStatusPreparing" xml:space="preserve">
|
||||
<value>Préparation de la mise à jour du modèle</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialised" xml:space="preserve">
|
||||
<value>Initialisé</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingManifest" xml:space="preserve">
|
||||
<value>Téléchargement du manifeste du modèle en cours</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingModel" xml:space="preserve">
|
||||
<value>Téléchargement du modèle</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialising" xml:space="preserve">
|
||||
<value>Initialisation du modèle et du classificateur</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledBadModel" xml:space="preserve">
|
||||
<value>Le signalement est désactivé car votre modèle de ML n'est pas fonctionnel au moment où ce message est affiché.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledSameClassification" xml:space="preserve">
|
||||
<value>Le signalement est désactivé car vous devez choisir une catégorisation différente de celle d'origine.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledItemLevel" xml:space="preserve">
|
||||
<value>Vous ne pouvez pas signaler ce message à cause du niveau d'objet requis.</value>
|
||||
</data>
|
||||
<data name="ReportModalOriginalClassification" xml:space="preserve">
|
||||
<value>La catégorisation originale du message : {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalSuggestedClassification" xml:space="preserve">
|
||||
<value>De quelle manière pensez-vous que ce message aurait dû être catégorisé ?</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledFilterNotEnabled" xml:space="preserve">
|
||||
<value>Le signalement est désactivé parce que vous ne filtriez pas cette catégorie de messages au moment où vous l'avez vu.</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOther" xml:space="preserve">
|
||||
<value>Autres</value>
|
||||
</data>
|
||||
<data name="ReportModalGoToCustomButton" xml:space="preserve">
|
||||
<value>Ouvrir les filtres de discussion personnalisés</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOtherDescription" xml:space="preserve">
|
||||
<value>Messages qui ne correspondent à aucune des autres catégories.</value>
|
||||
</data>
|
||||
</root>
|
292
NoSoliciting/Resources/Language.ja.resx
Normal file
292
NoSoliciting/Resources/Language.ja.resx
Normal file
|
@ -0,0 +1,292 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="OtherTab" xml:space="preserve">
|
||||
<value>その他</value>
|
||||
</data>
|
||||
<data name="OtherGameLanguage" xml:space="preserve">
|
||||
<value>Dalamudの言語設定ではなく、ゲームの言語設定を使用する</value>
|
||||
</data>
|
||||
<data name="ShowReportingWindow" xml:space="preserve">
|
||||
<value>報告ウィンドウを開く</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>{0} 設定</value>
|
||||
</data>
|
||||
<data name="ModelTab" xml:space="preserve">
|
||||
<value>モデル</value>
|
||||
</data>
|
||||
<data name="ModelTabVersion" xml:space="preserve">
|
||||
<value>バージョン: {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabStatus" xml:space="preserve">
|
||||
<value>モデルの状態: {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabError" xml:space="preserve">
|
||||
<value>最後に発生したエラー</value>
|
||||
</data>
|
||||
<data name="UpdateModel" xml:space="preserve">
|
||||
<value>モデルの更新</value>
|
||||
</data>
|
||||
<data name="FiltersTab" xml:space="preserve">
|
||||
<value>フィルタ</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning2" xml:space="preserve">
|
||||
<value>機械学習のモデルは特定のチャンネルの会話を想定して訓練されています。</value>
|
||||
</data>
|
||||
<data name="OtherFiltersTab" xml:space="preserve">
|
||||
<value>その他のフィルタ</value>
|
||||
</data>
|
||||
<data name="ChatFilters" xml:space="preserve">
|
||||
<value>チャットフィルタ</value>
|
||||
</data>
|
||||
<data name="PartyFinderFilters" xml:space="preserve">
|
||||
<value>パーティ募集フィルタ</value>
|
||||
</data>
|
||||
<data name="FilterIlvlPfs" xml:space="preserve">
|
||||
<value>アイテムレベルが最大値を超える募集をフィルタリング</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>カスタムパーティ募集フィルタを有効化</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>フィルタするキーワード</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>フィルタする正規表現</value>
|
||||
</data>
|
||||
<data name="SaveFilters" xml:space="preserve">
|
||||
<value>フィルタの保存</value>
|
||||
</data>
|
||||
<data name="Reporting" xml:space="preserve">
|
||||
<value>{0} 誤りの報告</value>
|
||||
</data>
|
||||
<data name="ReportStatusFailure" xml:space="preserve">
|
||||
<value>送信失敗</value>
|
||||
</data>
|
||||
<data name="ReportStatusSuccessful" xml:space="preserve">
|
||||
<value>送信成功</value>
|
||||
</data>
|
||||
<data name="LogFilteredMessages" xml:space="preserve">
|
||||
<value>テキストメッセージのフィルタリングログを有効化</value>
|
||||
</data>
|
||||
<data name="LogFilteredPfs" xml:space="preserve">
|
||||
<value>パーティ募集のフィルタリングログを有効化</value>
|
||||
</data>
|
||||
<data name="ReportStatusInProgress" xml:space="preserve">
|
||||
<value>送信中</value>
|
||||
</data>
|
||||
<data name="ReportStatusUnknown" xml:space="preserve">
|
||||
<value>不明</value>
|
||||
</data>
|
||||
<data name="ReportStatusMessage" xml:space="preserve">
|
||||
<value>最後の報告の状態: {0}</value>
|
||||
</data>
|
||||
<data name="ReportChatTab" xml:space="preserve">
|
||||
<value>チャット</value>
|
||||
</data>
|
||||
<data name="ReportColumnChannel" xml:space="preserve">
|
||||
<value>チャンネル</value>
|
||||
</data>
|
||||
<data name="ReportColumnReason" xml:space="preserve">
|
||||
<value>理由</value>
|
||||
</data>
|
||||
<data name="ReportColumnSender" xml:space="preserve">
|
||||
<value>発言者</value>
|
||||
</data>
|
||||
<data name="ReportColumnMessage" xml:space="preserve">
|
||||
<value>内容</value>
|
||||
</data>
|
||||
<data name="ReportPartyFinderTab" xml:space="preserve">
|
||||
<value>パーティ募集</value>
|
||||
</data>
|
||||
<data name="ReportColumnHost" xml:space="preserve">
|
||||
<value>募集者</value>
|
||||
</data>
|
||||
<data name="ReportColumnDescription" xml:space="preserve">
|
||||
<value>説明</value>
|
||||
</data>
|
||||
<data name="ReportModalTitle" xml:space="preserve">
|
||||
<value>{0}に報告</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp2" xml:space="preserve">
|
||||
<value>NoSolicitingは英語のメッセージにのみ有効です。 他の言語でメッセージを報告しないでください。</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledCustom" xml:space="preserve">
|
||||
<value>カスタムフィルタでフィルタリングされたメッセージを報告することはできません。</value>
|
||||
</data>
|
||||
<data name="ReportModalReport" xml:space="preserve">
|
||||
<value>報告</value>
|
||||
</data>
|
||||
<data name="ReportModalCopy" xml:space="preserve">
|
||||
<value>クリップボードにコピー</value>
|
||||
</data>
|
||||
<data name="ReportModalCancel" xml:space="preserve">
|
||||
<value>キャンセル</value>
|
||||
</data>
|
||||
<data name="TradeCategory" xml:space="preserve">
|
||||
<value>トレード広告</value>
|
||||
</data>
|
||||
<data name="NormalCategory" xml:space="preserve">
|
||||
<value>通常メッセージ</value>
|
||||
</data>
|
||||
<data name="PhishingCategory" xml:space="preserve">
|
||||
<value>フィッシングメッセージ</value>
|
||||
</data>
|
||||
<data name="RmtContentCategory" xml:space="preserve">
|
||||
<value>RMT (コンテンツ)</value>
|
||||
</data>
|
||||
<data name="RmtGilCategory" xml:space="preserve">
|
||||
<value>RMT (ギル)</value>
|
||||
</data>
|
||||
<data name="StaticCategory" xml:space="preserve">
|
||||
<value>固定パーティ募集</value>
|
||||
</data>
|
||||
<data name="CommunityCategory" xml:space="preserve">
|
||||
<value>コミュニティ広告</value>
|
||||
</data>
|
||||
<data name="StaticSubCategory" xml:space="preserve">
|
||||
<value>固定パーティ補充の募集</value>
|
||||
</data>
|
||||
<data name="FreeCompanyDescription" xml:space="preserve">
|
||||
<value>フリーカンパニーの募集広告</value>
|
||||
</data>
|
||||
<data name="NormalDescription" xml:space="preserve">
|
||||
<value>フィルタリングされるべきでない通常のメッセージ</value>
|
||||
</data>
|
||||
<data name="PhishingDescription" xml:space="preserve">
|
||||
<value>アカウントを盗むために、人を騙してアカウント情報を明かそうとするメッセージ</value>
|
||||
</data>
|
||||
<data name="RmtContentDescription" xml:space="preserve">
|
||||
<value>リアルマネーを使ったコンテンツの取引</value>
|
||||
</data>
|
||||
<data name="RoleplayingDescription" xml:space="preserve">
|
||||
<value>個人的なRP、RPコミュニティ、会場、その他ロールプレイに関連するものの広告</value>
|
||||
</data>
|
||||
<data name="StaticDescription" xml:space="preserve">
|
||||
<value>固定パーティのメンバー募集や固定パーティを探すプレイヤー</value>
|
||||
</data>
|
||||
<data name="StaticSubDescription" xml:space="preserve">
|
||||
<value>固定パーティのメンバー補充募集</value>
|
||||
</data>
|
||||
<data name="ReportToNoSoliciting" xml:space="preserve">
|
||||
<value>NoSolicitingに報告</value>
|
||||
</data>
|
||||
<data name="ReportToastSuccess" xml:space="preserve">
|
||||
<value>{0}のパーティ募集メッセージが正常に報告されました。</value>
|
||||
</data>
|
||||
<data name="AdvancedMode" xml:space="preserve">
|
||||
<value>高度な設定</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning1" xml:space="preserve">
|
||||
<value>高度な設定の変更は、自分が何をしているのかを理解していない限り行わないでください。</value>
|
||||
</data>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>カスタムチャットフィルタを有効化</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>プライベート募集にフィルタを適用</value>
|
||||
</data>
|
||||
<data name="ReportHelp" xml:space="preserve">
|
||||
<value>以下のエントリをクリックすることで、誤分類を開発者に報告することができます。</value>
|
||||
</data>
|
||||
<data name="ReportColumnTimestamp" xml:space="preserve">
|
||||
<value>タイムスタンプ</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp1" xml:space="preserve">
|
||||
<value>このメッセージを報告することで、開発者はこのメッセージが誤って分類されたことを知ることができます。</value>
|
||||
</data>
|
||||
<data name="FreeCompanyCategory" xml:space="preserve">
|
||||
<value>FC広告</value>
|
||||
</data>
|
||||
<data name="RoleplayingCategory" xml:space="preserve">
|
||||
<value>ロールプレイ広告</value>
|
||||
</data>
|
||||
<data name="TradeDescription" xml:space="preserve">
|
||||
<value>ギルを対価にクラフターの製作を行う募集やレアアイテムの取引など、アイテムやサービスをギルと交換することを宣伝するメッセージ</value>
|
||||
</data>
|
||||
<data name="RmtGilDescription" xml:space="preserve">
|
||||
<value>リアルマネーを使ったアイテムやギルの取引</value>
|
||||
</data>
|
||||
<data name="CommunityDescription" xml:space="preserve">
|
||||
<value>一般的なDiscordサーバなどの汎用コミュニティの広告</value>
|
||||
</data>
|
||||
<data name="ReportToastFailure" xml:space="preserve">
|
||||
<value>{0}のパーティ募集メッセージの報告に失敗しました。</value>
|
||||
</data>
|
||||
<data name="ModelStatusUninitialised" xml:space="preserve">
|
||||
<value>未初期化</value>
|
||||
</data>
|
||||
<data name="ModelStatusPreparing" xml:space="preserve">
|
||||
<value>モデルの更新準備中</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingManifest" xml:space="preserve">
|
||||
<value>モデルマニフェストのダウンロード中</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingModel" xml:space="preserve">
|
||||
<value>モデルのダウンロード中</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialising" xml:space="preserve">
|
||||
<value>モデルと分類器の初期化中</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialised" xml:space="preserve">
|
||||
<value>準備完了</value>
|
||||
</data>
|
||||
</root>
|
319
NoSoliciting/Resources/Language.pt-BR.resx
Normal file
319
NoSoliciting/Resources/Language.pt-BR.resx
Normal file
|
@ -0,0 +1,319 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="OtherTab" xml:space="preserve">
|
||||
<value>Outros</value>
|
||||
</data>
|
||||
<data name="LogFilteredPfs" xml:space="preserve">
|
||||
<value>Registrar PFs filtrados</value>
|
||||
</data>
|
||||
<data name="LogFilteredMessages" xml:space="preserve">
|
||||
<value>Registrar mensagens filtradas</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Configurações de {0}</value>
|
||||
</data>
|
||||
<data name="AdvancedMode" xml:space="preserve">
|
||||
<value>Modo avançado</value>
|
||||
</data>
|
||||
<data name="ModelTab" xml:space="preserve">
|
||||
<value>Modelo</value>
|
||||
</data>
|
||||
<data name="ModelTabVersion" xml:space="preserve">
|
||||
<value>Versão: {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabStatus" xml:space="preserve">
|
||||
<value>Status do modelo: {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabError" xml:space="preserve">
|
||||
<value>Último erro: {0}</value>
|
||||
</data>
|
||||
<data name="UpdateModel" xml:space="preserve">
|
||||
<value>Atualizar modelo</value>
|
||||
</data>
|
||||
<data name="FiltersTab" xml:space="preserve">
|
||||
<value>Filtros</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning1" xml:space="preserve">
|
||||
<value>Não altere as configurações avançadas a não ser que você saiba o que está fazendo.</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning2" xml:space="preserve">
|
||||
<value>O modelo de machine learning foi treinado com certos canais em mente.</value>
|
||||
</data>
|
||||
<data name="OtherFiltersTab" xml:space="preserve">
|
||||
<value>Outros filtros</value>
|
||||
</data>
|
||||
<data name="ChatFilters" xml:space="preserve">
|
||||
<value>Filtros de Chat</value>
|
||||
</data>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>Ativar filtros de chat personalizados</value>
|
||||
</data>
|
||||
<data name="PartyFinderFilters" xml:space="preserve">
|
||||
<value>Filtros do Party Finder</value>
|
||||
</data>
|
||||
<data name="FilterIlvlPfs" xml:space="preserve">
|
||||
<value>Filtrar PFs com item level acima do máximo</value>
|
||||
</data>
|
||||
<data name="ShowReportingWindow" xml:space="preserve">
|
||||
<value>Mostrar janela de relato</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>Aplicar filtros a anúncios de Party Finder privado</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>Ativar filtros de Party Finder personalizados</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>Subtextos a filtrar</value>
|
||||
</data>
|
||||
<data name="SaveFilters" xml:space="preserve">
|
||||
<value>Salvar filtros</value>
|
||||
</data>
|
||||
<data name="Reporting" xml:space="preserve">
|
||||
<value>Relatar {0}</value>
|
||||
</data>
|
||||
<data name="ReportHelp" xml:space="preserve">
|
||||
<value>Clique em uma das entradas abaixo para reportá-la ao desenvolvedor como categorizada de forma errônea.</value>
|
||||
</data>
|
||||
<data name="ReportStatusFailure" xml:space="preserve">
|
||||
<value>Falha no envio</value>
|
||||
</data>
|
||||
<data name="ReportStatusSuccessful" xml:space="preserve">
|
||||
<value>enviado com sucesso</value>
|
||||
</data>
|
||||
<data name="ReportStatusInProgress" xml:space="preserve">
|
||||
<value>enviando</value>
|
||||
</data>
|
||||
<data name="ReportStatusUnknown" xml:space="preserve">
|
||||
<value>desconhecido</value>
|
||||
</data>
|
||||
<data name="ReportChatTab" xml:space="preserve">
|
||||
<value>Chat</value>
|
||||
</data>
|
||||
<data name="ReportColumnTimestamp" xml:space="preserve">
|
||||
<value>Carimbo de data / hora</value>
|
||||
</data>
|
||||
<data name="ReportColumnChannel" xml:space="preserve">
|
||||
<value>Canal</value>
|
||||
</data>
|
||||
<data name="ReportColumnReason" xml:space="preserve">
|
||||
<value>Motivo</value>
|
||||
</data>
|
||||
<data name="ReportColumnSender" xml:space="preserve">
|
||||
<value>Remetente</value>
|
||||
</data>
|
||||
<data name="ReportColumnMessage" xml:space="preserve">
|
||||
<value>Mensagem</value>
|
||||
</data>
|
||||
<data name="ReportPartyFinderTab" xml:space="preserve">
|
||||
<value>Party Finder</value>
|
||||
</data>
|
||||
<data name="ReportColumnHost" xml:space="preserve">
|
||||
<value>Anfitrião</value>
|
||||
</data>
|
||||
<data name="ReportColumnDescription" xml:space="preserve">
|
||||
<value>Descrição</value>
|
||||
</data>
|
||||
<data name="ReportModalTitle" xml:space="preserve">
|
||||
<value>Relatar a(ao) {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledCustom" xml:space="preserve">
|
||||
<value>Voçê não pode relatar as mensagens filtradas por causa de um filtro personalizado.</value>
|
||||
</data>
|
||||
<data name="ReportModalReport" xml:space="preserve">
|
||||
<value>Relatar</value>
|
||||
</data>
|
||||
<data name="ReportModalCopy" xml:space="preserve">
|
||||
<value>Copiar para a área de transferência</value>
|
||||
</data>
|
||||
<data name="ReportModalCancel" xml:space="preserve">
|
||||
<value>Cancelar</value>
|
||||
</data>
|
||||
<data name="TradeCategory" xml:space="preserve">
|
||||
<value>Anúncios propondo trocas</value>
|
||||
</data>
|
||||
<data name="FreeCompanyCategory" xml:space="preserve">
|
||||
<value>Anúncios de Free Company</value>
|
||||
</data>
|
||||
<data name="NormalCategory" xml:space="preserve">
|
||||
<value>Mensagens normais</value>
|
||||
</data>
|
||||
<data name="PhishingCategory" xml:space="preserve">
|
||||
<value>Mensagens de Phishing</value>
|
||||
</data>
|
||||
<data name="RmtContentCategory" xml:space="preserve">
|
||||
<value>RMT (conteúdo)</value>
|
||||
</data>
|
||||
<data name="RmtGilCategory" xml:space="preserve">
|
||||
<value>RMT (gil)</value>
|
||||
</data>
|
||||
<data name="StaticCategory" xml:space="preserve">
|
||||
<value>Recrutamentos para Statics</value>
|
||||
</data>
|
||||
<data name="CommunityCategory" xml:space="preserve">
|
||||
<value>Anúncios de Comunidades</value>
|
||||
</data>
|
||||
<data name="StaticSubCategory" xml:space="preserve">
|
||||
<value>Substitutos para Statics</value>
|
||||
</data>
|
||||
<data name="FreeCompanyDescription" xml:space="preserve">
|
||||
<value>Propagandas de Free Companies</value>
|
||||
</data>
|
||||
<data name="NormalDescription" xml:space="preserve">
|
||||
<value>Mensagens normais que não devem ser filtradas</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingManifest" xml:space="preserve">
|
||||
<value>Baixando manifest de modelos</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingModel" xml:space="preserve">
|
||||
<value>Baixando modelo</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialising" xml:space="preserve">
|
||||
<value>Inicializando modelo e classificador</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialised" xml:space="preserve">
|
||||
<value>Inicializado</value>
|
||||
</data>
|
||||
<data name="PhishingDescription" xml:space="preserve">
|
||||
<value>Mensagens tentando enganar você para que você revele dados de sua conta para poder roubá-la</value>
|
||||
</data>
|
||||
<data name="RmtContentDescription" xml:space="preserve">
|
||||
<value>Conteúdo que envolva troca por dinheiro real (prática conhecida como venda de conteúdo)</value>
|
||||
</data>
|
||||
<data name="RmtGilDescription" xml:space="preserve">
|
||||
<value>Troca por dinheiro real que envolva gil ou itens (também conhecidos como bots de RMT)</value>
|
||||
</data>
|
||||
<data name="RoleplayingDescription" xml:space="preserve">
|
||||
<value>Propaganda de RP pessoal, comunidades ou locais de RP ou qualquer coisa que envolva roleplaying</value>
|
||||
</data>
|
||||
<data name="StaticDescription" xml:space="preserve">
|
||||
<value>Statics procurando membros ou jogadores procurando por um Static</value>
|
||||
</data>
|
||||
<data name="CommunityDescription" xml:space="preserve">
|
||||
<value>Propaganda de comunidades, em geral servidores no Discord</value>
|
||||
</data>
|
||||
<data name="StaticSubDescription" xml:space="preserve">
|
||||
<value>Statics procurando tapa-buracos para membros</value>
|
||||
</data>
|
||||
<data name="ReportToNoSoliciting" xml:space="preserve">
|
||||
<value>Relatar ao NoSoliciting</value>
|
||||
</data>
|
||||
<data name="ReportToastSuccess" xml:space="preserve">
|
||||
<value>Anúncio de Party Finder criado pelo jogador {0} relatado com sucesso.</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>Expressões regulares a filtrar</value>
|
||||
</data>
|
||||
<data name="ReportStatusMessage" xml:space="preserve">
|
||||
<value>Status do último relato: {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp1" xml:space="preserve">
|
||||
<value>Relatar essa mensagem vai permtir que o desenvolvedor saiba por que você pensa que essa mensagem foi classificada erroneamente.</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp2" xml:space="preserve">
|
||||
<value>NoSoliciting só funciona para mensagens em Inglês. Não relate mensagens em outras línguas.</value>
|
||||
</data>
|
||||
<data name="RoleplayingCategory" xml:space="preserve">
|
||||
<value>Anúncios de Roleplaying</value>
|
||||
</data>
|
||||
<data name="TradeDescription" xml:space="preserve">
|
||||
<value>Mensagens de propaganda de troca de itens ou serviços por gil, tais como omnicrafters oferecendo serviços ou pessoas vendendo itens raros fora do market board</value>
|
||||
</data>
|
||||
<data name="ReportToastFailure" xml:space="preserve">
|
||||
<value>Falha ao relatar anúncio de Party Finder do anfitrião {0}.</value>
|
||||
</data>
|
||||
<data name="OtherGameLanguage" xml:space="preserve">
|
||||
<value>Usar a configuração de idioma do jogo em vez da do Dalamud</value>
|
||||
</data>
|
||||
<data name="ModelStatusUninitialised" xml:space="preserve">
|
||||
<value>Não inicializado</value>
|
||||
</data>
|
||||
<data name="ModelStatusPreparing" xml:space="preserve">
|
||||
<value>Preparando para atualizar modelo</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOtherDescription" xml:space="preserve">
|
||||
<value>Mensagens que não caiam em outras categorias.</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOther" xml:space="preserve">
|
||||
<value>Outros</value>
|
||||
</data>
|
||||
<data name="ReportModalGoToCustomButton" xml:space="preserve">
|
||||
<value>Ir para filtros personalizados</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledBadModel" xml:space="preserve">
|
||||
<value>Função relatar está desabilitada por que seu modelo de ML não estava funcionando quando você viu esta mensagem.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledSameClassification" xml:space="preserve">
|
||||
<value>Função relatar desabilitada por que você precisa escolher uma classificação diferente da original.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledItemLevel" xml:space="preserve">
|
||||
<value>Você não pode relatar mensagens filtradas por causa de item level.</value>
|
||||
</data>
|
||||
<data name="ReportModalOriginalClassification" xml:space="preserve">
|
||||
<value>Classificação original desta mensagem: {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalSuggestedClassification" xml:space="preserve">
|
||||
<value>Como você acha que esta mensagem deveria ter sido classificada?</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledFilterNotEnabled" xml:space="preserve">
|
||||
<value>Função relatar desabilitada por que você não estava filtrando este tipo de mensagem quando a viu.</value>
|
||||
</data>
|
||||
</root>
|
285
NoSoliciting/Resources/Language.resx
Executable file
285
NoSoliciting/Resources/Language.resx
Executable file
|
@ -0,0 +1,285 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="OtherTab" xml:space="preserve">
|
||||
<value>Other</value>
|
||||
</data>
|
||||
<data name="LogFilteredPfs" xml:space="preserve">
|
||||
<value>Log filtered PFs</value>
|
||||
</data>
|
||||
<data name="LogFilteredMessages" xml:space="preserve">
|
||||
<value>Log filtered messages</value>
|
||||
</data>
|
||||
<data name="ShowReportingWindow" xml:space="preserve">
|
||||
<value>Show reporting window</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>{0} settings</value>
|
||||
</data>
|
||||
<data name="AdvancedMode" xml:space="preserve">
|
||||
<value>Advanced mode</value>
|
||||
</data>
|
||||
<data name="ModelTab" xml:space="preserve">
|
||||
<value>Model</value>
|
||||
</data>
|
||||
<data name="ModelTabVersion" xml:space="preserve">
|
||||
<value>Version: {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabStatus" xml:space="preserve">
|
||||
<value>Model status: {0}</value>
|
||||
</data>
|
||||
<data name="ModelTabError" xml:space="preserve">
|
||||
<value>Last error: {0}</value>
|
||||
</data>
|
||||
<data name="UpdateModel" xml:space="preserve">
|
||||
<value>Update model</value>
|
||||
</data>
|
||||
<data name="FiltersTab" xml:space="preserve">
|
||||
<value>Filters</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning1" xml:space="preserve">
|
||||
<value>Do not change advanced settings unless you know what you are doing.</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning2" xml:space="preserve">
|
||||
<value>The machine learning model was trained with certain channels in mind.</value>
|
||||
</data>
|
||||
<data name="OtherFiltersTab" xml:space="preserve">
|
||||
<value>Other filters</value>
|
||||
</data>
|
||||
<data name="ChatFilters" xml:space="preserve">
|
||||
<value>Chat filters</value>
|
||||
</data>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>Enable custom chat filters</value>
|
||||
</data>
|
||||
<data name="PartyFinderFilters" xml:space="preserve">
|
||||
<value>Party Finder filters</value>
|
||||
</data>
|
||||
<data name="FilterIlvlPfs" xml:space="preserve">
|
||||
<value>Filter PFs with item level above maximum</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>Apply filters to private Party Finder listings</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>Enable custom Party Finder filters</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>Substrings to filter</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>Regular expressions to filter</value>
|
||||
</data>
|
||||
<data name="SaveFilters" xml:space="preserve">
|
||||
<value>Save filters</value>
|
||||
</data>
|
||||
<data name="Reporting" xml:space="preserve">
|
||||
<value>{0} reporting</value>
|
||||
</data>
|
||||
<data name="ReportHelp" xml:space="preserve">
|
||||
<value>Click on one of the entries below to report it to the developer as miscategorised.</value>
|
||||
</data>
|
||||
<data name="ReportStatusFailure" xml:space="preserve">
|
||||
<value>failed to send</value>
|
||||
</data>
|
||||
<data name="ReportStatusSuccessful" xml:space="preserve">
|
||||
<value>sent successfully</value>
|
||||
</data>
|
||||
<data name="ReportStatusInProgress" xml:space="preserve">
|
||||
<value>sending</value>
|
||||
</data>
|
||||
<data name="ReportStatusUnknown" xml:space="preserve">
|
||||
<value>unknown</value>
|
||||
</data>
|
||||
<data name="ReportStatusMessage" xml:space="preserve">
|
||||
<value>Last report status: {0}</value>
|
||||
</data>
|
||||
<data name="ReportChatTab" xml:space="preserve">
|
||||
<value>Chat</value>
|
||||
</data>
|
||||
<data name="ReportColumnTimestamp" xml:space="preserve">
|
||||
<value>Timestamp</value>
|
||||
</data>
|
||||
<data name="ReportColumnChannel" xml:space="preserve">
|
||||
<value>Channel</value>
|
||||
</data>
|
||||
<data name="ReportColumnReason" xml:space="preserve">
|
||||
<value>Reason</value>
|
||||
</data>
|
||||
<data name="ReportColumnSender" xml:space="preserve">
|
||||
<value>Sender</value>
|
||||
</data>
|
||||
<data name="ReportColumnMessage" xml:space="preserve">
|
||||
<value>Message</value>
|
||||
</data>
|
||||
<data name="ReportPartyFinderTab" xml:space="preserve">
|
||||
<value>Party Finder</value>
|
||||
</data>
|
||||
<data name="ReportColumnHost" xml:space="preserve">
|
||||
<value>Host</value>
|
||||
</data>
|
||||
<data name="ReportColumnDescription" xml:space="preserve">
|
||||
<value>Description</value>
|
||||
</data>
|
||||
<data name="ReportModalTitle" xml:space="preserve">
|
||||
<value>Report to {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp1" xml:space="preserve">
|
||||
<value>Reporting this message will let the developer know that you think this message was incorrectly classified.</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp2" xml:space="preserve">
|
||||
<value>NoSoliciting only works for English messages. Do not report non-English messages.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledCustom" xml:space="preserve">
|
||||
<value>You cannot report messages filtered because of a custom filter.</value>
|
||||
</data>
|
||||
<data name="ReportModalReport" xml:space="preserve">
|
||||
<value>Report</value>
|
||||
</data>
|
||||
<data name="ReportModalCopy" xml:space="preserve">
|
||||
<value>Copy to clipboard</value>
|
||||
</data>
|
||||
<data name="ReportModalCancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="TradeCategory" xml:space="preserve">
|
||||
<value>Trade ads</value>
|
||||
</data>
|
||||
<data name="FreeCompanyCategory" xml:space="preserve">
|
||||
<value>Free Company ads</value>
|
||||
</data>
|
||||
<data name="NormalCategory" xml:space="preserve">
|
||||
<value>Normal messages</value>
|
||||
</data>
|
||||
<data name="PhishingCategory" xml:space="preserve">
|
||||
<value>Phishing messages</value>
|
||||
</data>
|
||||
<data name="RmtContentCategory" xml:space="preserve">
|
||||
<value>RMT (content)</value>
|
||||
</data>
|
||||
<data name="RmtGilCategory" xml:space="preserve">
|
||||
<value>RMT (gil)</value>
|
||||
</data>
|
||||
<data name="RoleplayingCategory" xml:space="preserve">
|
||||
<value>Roleplaying ads</value>
|
||||
</data>
|
||||
<data name="StaticCategory" xml:space="preserve">
|
||||
<value>Static recruitment</value>
|
||||
</data>
|
||||
<data name="CommunityCategory" xml:space="preserve">
|
||||
<value>Community ads</value>
|
||||
</data>
|
||||
<data name="StaticSubCategory" xml:space="preserve">
|
||||
<value>Static substitutes</value>
|
||||
</data>
|
||||
<data name="TradeDescription" xml:space="preserve">
|
||||
<value>Messages advertising trading items or services for gil, such as omnicrafters looking for work or people selling rare items off the market board</value>
|
||||
</data>
|
||||
<data name="FreeCompanyDescription" xml:space="preserve">
|
||||
<value>Advertisements for Free Companies</value>
|
||||
</data>
|
||||
<data name="NormalDescription" xml:space="preserve">
|
||||
<value>Normal messages that should not be filtered</value>
|
||||
</data>
|
||||
<data name="PhishingDescription" xml:space="preserve">
|
||||
<value>Messages trying to trick you into revealing your account details in order to steal your account</value>
|
||||
</data>
|
||||
<data name="RmtContentDescription" xml:space="preserve">
|
||||
<value>Real-money trade involving content (also known as content sellers)</value>
|
||||
</data>
|
||||
<data name="RmtGilDescription" xml:space="preserve">
|
||||
<value>Real-money trade involving gil or items (also known as RMT bots)</value>
|
||||
</data>
|
||||
<data name="RoleplayingDescription" xml:space="preserve">
|
||||
<value>Advertisements for personal RP, RP communities, venues, or anything else related to roleplaying</value>
|
||||
</data>
|
||||
<data name="StaticDescription" xml:space="preserve">
|
||||
<value>Statics looking for members or players looking for a static</value>
|
||||
</data>
|
||||
<data name="CommunityDescription" xml:space="preserve">
|
||||
<value>Advertisements for general-purpose communities, generally Discord servers</value>
|
||||
</data>
|
||||
<data name="StaticSubDescription" xml:space="preserve">
|
||||
<value>Statics looking for fill-ins of missing members for clears</value>
|
||||
</data>
|
||||
<data name="ReportToNoSoliciting" xml:space="preserve">
|
||||
<value>Report to NoSoliciting</value>
|
||||
</data>
|
||||
<data name="ReportToastSuccess" xml:space="preserve">
|
||||
<value>Party Finder listing hosted by {0} reported successfully.</value>
|
||||
</data>
|
||||
<data name="ReportToastFailure" xml:space="preserve">
|
||||
<value>Failed to report Party Finder listing hosted by {0}.</value>
|
||||
</data>
|
||||
<data name="OtherGameLanguage" xml:space="preserve">
|
||||
<value>Use the game's language setting instead of Dalamud's</value>
|
||||
</data>
|
||||
<data name="ModelStatusUninitialised" xml:space="preserve">
|
||||
<value>Uninitialised</value>
|
||||
</data>
|
||||
<data name="ModelStatusPreparing" xml:space="preserve">
|
||||
<value>Preparing to update model</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingManifest" xml:space="preserve">
|
||||
<value>Downloading model manifest</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingModel" xml:space="preserve">
|
||||
<value>Downloading model</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialising" xml:space="preserve">
|
||||
<value>Initialising model and classifier</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialised" xml:space="preserve">
|
||||
<value>Initialised</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledBadModel" xml:space="preserve">
|
||||
<value>Reporting is disabled because your ML model was not functioning when you saw this message.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledSameClassification" xml:space="preserve">
|
||||
<value>Reporting is disabled because you must choose a different classification than the original.</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledItemLevel" xml:space="preserve">
|
||||
<value>You cannot report messages filtered because of item level.</value>
|
||||
</data>
|
||||
<data name="ReportModalOriginalClassification" xml:space="preserve">
|
||||
<value>This message's original classification: {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalSuggestedClassification" xml:space="preserve">
|
||||
<value>How do you think this message should have been classified?</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledFilterNotEnabled" xml:space="preserve">
|
||||
<value>Reporting is disabled because you weren't filtering for this kind of message at the time you saw it.</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOther" xml:space="preserve">
|
||||
<value>Other</value>
|
||||
</data>
|
||||
<data name="ReportModalGoToCustomButton" xml:space="preserve">
|
||||
<value>Go to custom filters</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOtherDescription" xml:space="preserve">
|
||||
<value>Messages that don't fall under any of the other categories.</value>
|
||||
</data>
|
||||
<data name="FluffCategory" xml:space="preserve">
|
||||
<value>Fluff</value>
|
||||
</data>
|
||||
<data name="FluffDescription" xml:space="preserve">
|
||||
<value>Messages that don't fall into other categories and are not for content (Party Finder)</value>
|
||||
</data>
|
||||
</root>
|
319
NoSoliciting/Resources/Language.zh-hans.resx
Normal file
319
NoSoliciting/Resources/Language.zh-hans.resx
Normal file
|
@ -0,0 +1,319 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="OtherGameLanguage" xml:space="preserve">
|
||||
<value>使用游戏语言而不是 Dalamud 的语言作为插件语言</value>
|
||||
</data>
|
||||
<data name="LogFilteredPfs" xml:space="preserve">
|
||||
<value>启用队员招募过滤日志</value>
|
||||
</data>
|
||||
<data name="ShowReportingWindow" xml:space="preserve">
|
||||
<value>回报错误</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>{0} 设置</value>
|
||||
</data>
|
||||
<data name="AdvancedMode" xml:space="preserve">
|
||||
<value>高级模式</value>
|
||||
</data>
|
||||
<data name="ModelTab" xml:space="preserve">
|
||||
<value>机器学习模型</value>
|
||||
</data>
|
||||
<data name="ModelTabVersion" xml:space="preserve">
|
||||
<value>版本:{0}</value>
|
||||
</data>
|
||||
<data name="ModelTabStatus" xml:space="preserve">
|
||||
<value>状态:{0}</value>
|
||||
</data>
|
||||
<data name="ModelTabError" xml:space="preserve">
|
||||
<value>最后一个错误:{0}</value>
|
||||
</data>
|
||||
<data name="UpdateModel" xml:space="preserve">
|
||||
<value>更新模型</value>
|
||||
</data>
|
||||
<data name="FiltersTab" xml:space="preserve">
|
||||
<value>过滤规则</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning2" xml:space="preserve">
|
||||
<value>机器学习模型已经对部分频道进行了训练。</value>
|
||||
</data>
|
||||
<data name="OtherFiltersTab" xml:space="preserve">
|
||||
<value>其他过滤规则</value>
|
||||
</data>
|
||||
<data name="ChatFilters" xml:space="preserve">
|
||||
<value>聊天消息过滤</value>
|
||||
</data>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>启用自定义聊天消息过滤</value>
|
||||
</data>
|
||||
<data name="PartyFinderFilters" xml:space="preserve">
|
||||
<value>队员招募过滤</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>应用过滤规则至带密码的招募</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>启用自定义队员招募过滤</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>过滤关键词</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>过滤正则表达式</value>
|
||||
</data>
|
||||
<data name="SaveFilters" xml:space="preserve">
|
||||
<value>保存过滤规则</value>
|
||||
</data>
|
||||
<data name="Reporting" xml:space="preserve">
|
||||
<value>{0} 错误报告</value>
|
||||
</data>
|
||||
<data name="ReportStatusFailure" xml:space="preserve">
|
||||
<value>发送失败</value>
|
||||
</data>
|
||||
<data name="ReportStatusSuccessful" xml:space="preserve">
|
||||
<value>发送成功</value>
|
||||
</data>
|
||||
<data name="ReportStatusInProgress" xml:space="preserve">
|
||||
<value>发送中</value>
|
||||
</data>
|
||||
<data name="ReportStatusUnknown" xml:space="preserve">
|
||||
<value>未知</value>
|
||||
</data>
|
||||
<data name="ReportStatusMessage" xml:space="preserve">
|
||||
<value>最后报告状态:{0}</value>
|
||||
</data>
|
||||
<data name="ReportChatTab" xml:space="preserve">
|
||||
<value>聊天消息</value>
|
||||
</data>
|
||||
<data name="ReportColumnTimestamp" xml:space="preserve">
|
||||
<value>时间</value>
|
||||
</data>
|
||||
<data name="ReportColumnChannel" xml:space="preserve">
|
||||
<value>频道</value>
|
||||
</data>
|
||||
<data name="ReportColumnReason" xml:space="preserve">
|
||||
<value>原因</value>
|
||||
</data>
|
||||
<data name="ReportColumnSender" xml:space="preserve">
|
||||
<value>发送者</value>
|
||||
</data>
|
||||
<data name="ReportColumnMessage" xml:space="preserve">
|
||||
<value>消息内容</value>
|
||||
</data>
|
||||
<data name="ReportPartyFinderTab" xml:space="preserve">
|
||||
<value>队员招募</value>
|
||||
</data>
|
||||
<data name="ReportColumnHost" xml:space="preserve">
|
||||
<value>招募者</value>
|
||||
</data>
|
||||
<data name="ReportColumnDescription" xml:space="preserve">
|
||||
<value>说明</value>
|
||||
</data>
|
||||
<data name="ReportModalTitle" xml:space="preserve">
|
||||
<value>报告给 {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledCustom" xml:space="preserve">
|
||||
<value>你不能报告被自定义过滤规则过滤的消息。</value>
|
||||
</data>
|
||||
<data name="ReportModalReport" xml:space="preserve">
|
||||
<value>报告</value>
|
||||
</data>
|
||||
<data name="ReportModalCopy" xml:space="preserve">
|
||||
<value>复制到剪贴板</value>
|
||||
</data>
|
||||
<data name="ReportModalCancel" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="TradeCategory" xml:space="preserve">
|
||||
<value>交易广告</value>
|
||||
</data>
|
||||
<data name="FreeCompanyCategory" xml:space="preserve">
|
||||
<value>部队广告</value>
|
||||
</data>
|
||||
<data name="StaticSubCategory" xml:space="preserve">
|
||||
<value>固定队补位招募</value>
|
||||
</data>
|
||||
<data name="NormalCategory" xml:space="preserve">
|
||||
<value>普通消息</value>
|
||||
</data>
|
||||
<data name="PhishingCategory" xml:space="preserve">
|
||||
<value>钓鱼消息</value>
|
||||
</data>
|
||||
<data name="RmtContentCategory" xml:space="preserve">
|
||||
<value>RMT(副本)</value>
|
||||
</data>
|
||||
<data name="RmtGilCategory" xml:space="preserve">
|
||||
<value>RMT(金币)</value>
|
||||
</data>
|
||||
<data name="RoleplayingCategory" xml:space="preserve">
|
||||
<value>角色扮演广告</value>
|
||||
</data>
|
||||
<data name="TradeDescription" xml:space="preserve">
|
||||
<value>宣传使用金币交易物品或代打的消息,例如寻找工作或贩卖稀有物品</value>
|
||||
</data>
|
||||
<data name="FreeCompanyDescription" xml:space="preserve">
|
||||
<value>部队广告</value>
|
||||
</data>
|
||||
<data name="NormalDescription" xml:space="preserve">
|
||||
<value>普通消息,不应该被过滤</value>
|
||||
</data>
|
||||
<data name="PhishingDescription" xml:space="preserve">
|
||||
<value>试图欺骗你透露账号信息以进行盗号等行为的消息</value>
|
||||
</data>
|
||||
<data name="RmtContentDescription" xml:space="preserve">
|
||||
<value>使用现实货币进行/招募代打</value>
|
||||
</data>
|
||||
<data name="RmtGilDescription" xml:space="preserve">
|
||||
<value>使用现实货币购买/售卖金币或物品</value>
|
||||
</data>
|
||||
<data name="RoleplayingDescription" xml:space="preserve">
|
||||
<value>宣传个人RP、RP团体、场所或其他与角色扮演有关的消息</value>
|
||||
</data>
|
||||
<data name="StaticDescription" xml:space="preserve">
|
||||
<value>固定队寻找队友或玩家正在寻找固定队</value>
|
||||
</data>
|
||||
<data name="CommunityDescription" xml:space="preserve">
|
||||
<value>宣传玩家社区/团体的广告,例如 Discord 服务器等</value>
|
||||
</data>
|
||||
<data name="StaticSubDescription" xml:space="preserve">
|
||||
<value>固定队寻找补位成员</value>
|
||||
</data>
|
||||
<data name="ReportToNoSoliciting" xml:space="preserve">
|
||||
<value>报告给 NoSoliciting</value>
|
||||
</data>
|
||||
<data name="ReportToastSuccess" xml:space="preserve">
|
||||
<value>由 {0} 发起的队员招募已报告成功。</value>
|
||||
</data>
|
||||
<data name="ReportToastFailure" xml:space="preserve">
|
||||
<value>由 {0} 发起的队员招募报告失败。</value>
|
||||
</data>
|
||||
<data name="CommunityCategory" xml:space="preserve">
|
||||
<value>社区广告</value>
|
||||
</data>
|
||||
<data name="StaticCategory" xml:space="preserve">
|
||||
<value>固定队招募</value>
|
||||
</data>
|
||||
<data name="OtherTab" xml:space="preserve">
|
||||
<value>其他</value>
|
||||
</data>
|
||||
<data name="LogFilteredMessages" xml:space="preserve">
|
||||
<value>启用文字消息过滤日志</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning1" xml:space="preserve">
|
||||
<value>除非你明确知道自己在干什么,否则请不要更改高级设置。</value>
|
||||
</data>
|
||||
<data name="FilterIlvlPfs" xml:space="preserve">
|
||||
<value>过滤装备品级高于最大值的招募</value>
|
||||
</data>
|
||||
<data name="ReportHelp" xml:space="preserve">
|
||||
<value>单击下方分类错误的项目以将其报告给开发者。</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp1" xml:space="preserve">
|
||||
<value>报告此消息将使开发者知道此消息被错误归类。</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp2" xml:space="preserve">
|
||||
<value>NoSoliciting 只对英文消息有效。请不要报告其他语言的消息。</value>
|
||||
</data>
|
||||
<data name="ModelStatusUninitialised" xml:space="preserve">
|
||||
<value>未初始化</value>
|
||||
</data>
|
||||
<data name="ModelStatusPreparing" xml:space="preserve">
|
||||
<value>准备更新中</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingModel" xml:space="preserve">
|
||||
<value>正在下载模型</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingManifest" xml:space="preserve">
|
||||
<value>正在获取模型信息</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialising" xml:space="preserve">
|
||||
<value>正在进行初始化</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialised" xml:space="preserve">
|
||||
<value>初始化完成</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledFilterNotEnabled" xml:space="preserve">
|
||||
<value>您暂时无法报告此消息。因为您在当时并没有过滤此类消息。</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledBadModel" xml:space="preserve">
|
||||
<value>您暂时无法报告此消息。因为机器学习模型在当时并未启用。</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledSameClassification" xml:space="preserve">
|
||||
<value>您暂时无法报告此消息。因为您必须选择与原始分类不同的分类。</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledItemLevel" xml:space="preserve">
|
||||
<value>您暂时无法报告此消息。因为它的平均品级实在是太高了!</value>
|
||||
</data>
|
||||
<data name="ReportModalOriginalClassification" xml:space="preserve">
|
||||
<value>此消息的原始分类:{0}</value>
|
||||
</data>
|
||||
<data name="ReportModalSuggestedClassification" xml:space="preserve">
|
||||
<value>您觉得此消息应当被归为何种分类?</value>
|
||||
</data>
|
||||
<data name="ReportModalGoToCustomButton" xml:space="preserve">
|
||||
<value>前往自定义过滤规则</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOther" xml:space="preserve">
|
||||
<value>其他</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOtherDescription" xml:space="preserve">
|
||||
<value>不属于任何类别的消息。</value>
|
||||
</data>
|
||||
</root>
|
319
NoSoliciting/Resources/Language.zh-hant.resx
Normal file
319
NoSoliciting/Resources/Language.zh-hant.resx
Normal file
|
@ -0,0 +1,319 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ReportStatusMessage" xml:space="preserve">
|
||||
<value>最後報告狀態:{0}</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp1" xml:space="preserve">
|
||||
<value>報告此消息將使開發人員知道此消息被錯誤歸類。</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledCustom" xml:space="preserve">
|
||||
<value>你不能報告被客製化過濾規則過濾的消息。</value>
|
||||
</data>
|
||||
<data name="OtherTab" xml:space="preserve">
|
||||
<value>其他</value>
|
||||
</data>
|
||||
<data name="ModelTab" xml:space="preserve">
|
||||
<value>機器學習模式</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning1" xml:space="preserve">
|
||||
<value>除非你明確知道自己在做甚麼,否則請不要更改高級設置。</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>應用過濾規則至帶密碼的招募</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>過濾正則表達式</value>
|
||||
</data>
|
||||
<data name="SaveFilters" xml:space="preserve">
|
||||
<value>保存過濾規則</value>
|
||||
</data>
|
||||
<data name="Reporting" xml:space="preserve">
|
||||
<value>{0} 錯誤報告</value>
|
||||
</data>
|
||||
<data name="ReportHelp" xml:space="preserve">
|
||||
<value>單擊下方分類錯誤的項目以將其報告給開發人員。</value>
|
||||
</data>
|
||||
<data name="ReportStatusFailure" xml:space="preserve">
|
||||
<value>發送失敗</value>
|
||||
</data>
|
||||
<data name="ReportStatusSuccessful" xml:space="preserve">
|
||||
<value>發送成功</value>
|
||||
</data>
|
||||
<data name="ReportStatusInProgress" xml:space="preserve">
|
||||
<value>發送中</value>
|
||||
</data>
|
||||
<data name="ReportStatusUnknown" xml:space="preserve">
|
||||
<value>未知</value>
|
||||
</data>
|
||||
<data name="ReportChatTab" xml:space="preserve">
|
||||
<value>聊天消息</value>
|
||||
</data>
|
||||
<data name="ReportColumnTimestamp" xml:space="preserve">
|
||||
<value>時間</value>
|
||||
</data>
|
||||
<data name="ReportColumnChannel" xml:space="preserve">
|
||||
<value>頻道</value>
|
||||
</data>
|
||||
<data name="ReportColumnReason" xml:space="preserve">
|
||||
<value>原因</value>
|
||||
</data>
|
||||
<data name="ReportColumnSender" xml:space="preserve">
|
||||
<value>發送者</value>
|
||||
</data>
|
||||
<data name="ReportColumnMessage" xml:space="preserve">
|
||||
<value>消息內容</value>
|
||||
</data>
|
||||
<data name="ReportPartyFinderTab" xml:space="preserve">
|
||||
<value>隊員招募</value>
|
||||
</data>
|
||||
<data name="ReportColumnHost" xml:space="preserve">
|
||||
<value>招募者</value>
|
||||
</data>
|
||||
<data name="ReportColumnDescription" xml:space="preserve">
|
||||
<value>說明</value>
|
||||
</data>
|
||||
<data name="ReportModalTitle" xml:space="preserve">
|
||||
<value>報告給 {0}</value>
|
||||
</data>
|
||||
<data name="ReportModalHelp2" xml:space="preserve">
|
||||
<value>NoSoliciting 只對英文消息有效。請不要報告其他語言的消息。</value>
|
||||
</data>
|
||||
<data name="ReportModalReport" xml:space="preserve">
|
||||
<value>報告</value>
|
||||
</data>
|
||||
<data name="LogFilteredPfs" xml:space="preserve">
|
||||
<value>啟用隊員招募過濾日誌</value>
|
||||
</data>
|
||||
<data name="LogFilteredMessages" xml:space="preserve">
|
||||
<value>啟用文字消息過濾日誌</value>
|
||||
</data>
|
||||
<data name="ShowReportingWindow" xml:space="preserve">
|
||||
<value>回報錯誤</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>{0} 設置</value>
|
||||
</data>
|
||||
<data name="AdvancedMode" xml:space="preserve">
|
||||
<value>高級模式</value>
|
||||
</data>
|
||||
<data name="ModelTabVersion" xml:space="preserve">
|
||||
<value>版本:{0}</value>
|
||||
</data>
|
||||
<data name="ModelTabStatus" xml:space="preserve">
|
||||
<value>狀態:{0}</value>
|
||||
</data>
|
||||
<data name="ModelTabError" xml:space="preserve">
|
||||
<value>最後一個錯誤:{0}</value>
|
||||
</data>
|
||||
<data name="UpdateModel" xml:space="preserve">
|
||||
<value>更新模型</value>
|
||||
</data>
|
||||
<data name="FiltersTab" xml:space="preserve">
|
||||
<value>過濾規則</value>
|
||||
</data>
|
||||
<data name="AdvancedWarning2" xml:space="preserve">
|
||||
<value>機器學習模型已經對部分頻道進行了訓練。</value>
|
||||
</data>
|
||||
<data name="OtherFiltersTab" xml:space="preserve">
|
||||
<value>其他過濾規則</value>
|
||||
</data>
|
||||
<data name="ChatFilters" xml:space="preserve">
|
||||
<value>聊天消息過濾</value>
|
||||
</data>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>啟用客製化聊天消息過濾</value>
|
||||
</data>
|
||||
<data name="PartyFinderFilters" xml:space="preserve">
|
||||
<value>隊員招募過濾</value>
|
||||
</data>
|
||||
<data name="FilterIlvlPfs" xml:space="preserve">
|
||||
<value>過濾裝備品級高於最大值的招募</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>啟用客製化隊員招募過濾</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>過濾關鍵詞</value>
|
||||
</data>
|
||||
<data name="ReportModalCopy" xml:space="preserve">
|
||||
<value>複製到剪貼板</value>
|
||||
</data>
|
||||
<data name="ReportModalCancel" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="TradeCategory" xml:space="preserve">
|
||||
<value>交易廣告</value>
|
||||
</data>
|
||||
<data name="FreeCompanyCategory" xml:space="preserve">
|
||||
<value>部隊廣告</value>
|
||||
</data>
|
||||
<data name="NormalCategory" xml:space="preserve">
|
||||
<value>普通消息</value>
|
||||
</data>
|
||||
<data name="PhishingCategory" xml:space="preserve">
|
||||
<value>釣魚消息</value>
|
||||
</data>
|
||||
<data name="RmtContentCategory" xml:space="preserve">
|
||||
<value>RMT(副本)</value>
|
||||
</data>
|
||||
<data name="RmtGilCategory" xml:space="preserve">
|
||||
<value>RMT(金幣)</value>
|
||||
</data>
|
||||
<data name="RoleplayingCategory" xml:space="preserve">
|
||||
<value>角色扮演廣告</value>
|
||||
</data>
|
||||
<data name="StaticCategory" xml:space="preserve">
|
||||
<value>固定隊招募</value>
|
||||
</data>
|
||||
<data name="CommunityCategory" xml:space="preserve">
|
||||
<value>社區廣告</value>
|
||||
</data>
|
||||
<data name="StaticSubCategory" xml:space="preserve">
|
||||
<value>固定隊補位招募</value>
|
||||
</data>
|
||||
<data name="TradeDescription" xml:space="preserve">
|
||||
<value>宣傳使用金幣交易物品或代打的消息,例如尋找工作或販賣稀有物品</value>
|
||||
</data>
|
||||
<data name="FreeCompanyDescription" xml:space="preserve">
|
||||
<value>宣傳部隊</value>
|
||||
</data>
|
||||
<data name="NormalDescription" xml:space="preserve">
|
||||
<value>普通消息,不應該被過濾</value>
|
||||
</data>
|
||||
<data name="PhishingDescription" xml:space="preserve">
|
||||
<value>試圖欺騙你透露賬號信息以進行盜號等行為的消息</value>
|
||||
</data>
|
||||
<data name="RmtContentDescription" xml:space="preserve">
|
||||
<value>使用現實貨幣進行/招募代打</value>
|
||||
</data>
|
||||
<data name="RmtGilDescription" xml:space="preserve">
|
||||
<value>使用現實貨幣購買/售賣金幣或物品</value>
|
||||
</data>
|
||||
<data name="RoleplayingDescription" xml:space="preserve">
|
||||
<value>宣傳個人RP、RP團體、場所或其他與角色扮演有關的消息</value>
|
||||
</data>
|
||||
<data name="StaticDescription" xml:space="preserve">
|
||||
<value>固定隊尋找隊友或玩家正在尋找固定隊</value>
|
||||
</data>
|
||||
<data name="CommunityDescription" xml:space="preserve">
|
||||
<value>宣傳玩家社區/團體的廣告,例如 Discord 伺服器等</value>
|
||||
</data>
|
||||
<data name="StaticSubDescription" xml:space="preserve">
|
||||
<value>固定隊尋找補位隊員</value>
|
||||
</data>
|
||||
<data name="ReportToNoSoliciting" xml:space="preserve">
|
||||
<value>報告給 NoSoliciting</value>
|
||||
</data>
|
||||
<data name="ReportToastSuccess" xml:space="preserve">
|
||||
<value>由 {0} 發起的隊員招募以報告成功。</value>
|
||||
</data>
|
||||
<data name="ReportToastFailure" xml:space="preserve">
|
||||
<value>由 {0} 發起的隊員招募報告失敗。</value>
|
||||
</data>
|
||||
<data name="OtherGameLanguage" xml:space="preserve">
|
||||
<value>使用遊戲語言而不是 Dalamud 的語言作為插件語言</value>
|
||||
</data>
|
||||
<data name="ModelStatusUninitialised" xml:space="preserve">
|
||||
<value>未初始化</value>
|
||||
</data>
|
||||
<data name="ModelStatusPreparing" xml:space="preserve">
|
||||
<value>準備更新中</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingManifest" xml:space="preserve">
|
||||
<value>正在獲取模型信息</value>
|
||||
</data>
|
||||
<data name="ModelStatusDownloadingModel" xml:space="preserve">
|
||||
<value>正在下載模型</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialising" xml:space="preserve">
|
||||
<value>正在進行初始化</value>
|
||||
</data>
|
||||
<data name="ModelStatusInitialised" xml:space="preserve">
|
||||
<value>初始化完成</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledBadModel" xml:space="preserve">
|
||||
<value>您暫時無法報告此消息。因為機器學習模型在當時並未啟用。</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledSameClassification" xml:space="preserve">
|
||||
<value>您暫時無法報告此消息。因為您必須選擇與原始分類不同的分類。</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledItemLevel" xml:space="preserve">
|
||||
<value>您暫時無法報告此消息。因為它的平均品級太高了。</value>
|
||||
</data>
|
||||
<data name="ReportModalOriginalClassification" xml:space="preserve">
|
||||
<value>此消息的原始分類:{0}</value>
|
||||
</data>
|
||||
<data name="ReportModalSuggestedClassification" xml:space="preserve">
|
||||
<value>您覺得此消息應當被歸為何種分類?</value>
|
||||
</data>
|
||||
<data name="ReportModalDisabledFilterNotEnabled" xml:space="preserve">
|
||||
<value>您暫時無法報告此消息。因為您在當時並沒有過濾此類消息。</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOther" xml:space="preserve">
|
||||
<value>其他</value>
|
||||
</data>
|
||||
<data name="ReportModalGoToCustomButton" xml:space="preserve">
|
||||
<value>前往客製化過濾規則</value>
|
||||
</data>
|
||||
<data name="ReportModalClassificationOtherDescription" xml:space="preserve">
|
||||
<value>不屬於任何類別的消息。</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.CodeAnalysis.FxCopAnalyzers" version="2.9.6" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="Microsoft.CodeAnalysis.VersionCheckAnalyzer" version="2.9.6" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="Microsoft.CodeQuality.Analyzers" version="2.9.6" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="Microsoft.NetCore.Analyzers" version="2.9.6" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="Microsoft.NetFramework.Analyzers" version="2.9.6" targetFramework="net48" developmentDependency="true" />
|
||||
</packages>
|
201
NoSoliciting/packages.lock.json
Normal file
201
NoSoliciting/packages.lock.json
Normal file
|
@ -0,0 +1,201 @@
|
|||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net7.0-windows7.0": {
|
||||
"Dalamud.ContextMenu": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.3.1, )",
|
||||
"resolved": "1.3.1",
|
||||
"contentHash": "ptAxut5PiLnzZ4G/KQdHJVcyklC/BF3otHJ7zYVUPiKBjsOCoF0n/6h2jK7e+8ev2Y1yAY3Wtx2GuXLFQgt9Uw=="
|
||||
},
|
||||
"DalamudPackager": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.1.12, )",
|
||||
"resolved": "2.1.12",
|
||||
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
|
||||
},
|
||||
"Fody": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.8.0, )",
|
||||
"resolved": "6.8.0",
|
||||
"contentHash": "hfZ/f8Mezt8aTkgv9nsvFdYoQ809/AqwsJlOGOPYIfBcG2aAIG3v3ex9d8ZqQuFYyMoucjRg4eKy3VleeGodKQ=="
|
||||
},
|
||||
"Microsoft.ML": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.0.1, )",
|
||||
"resolved": "2.0.1",
|
||||
"contentHash": "yD5kWZfisykwiGG66wJToSicY9MP847Grl8kLALQyqyiyQjr408ChdGW1JG7D0wHN7Zbs1KkCZvidi6pMFYR/A==",
|
||||
"dependencies": {
|
||||
"Microsoft.ML.CpuMath": "2.0.1",
|
||||
"Microsoft.ML.DataView": "2.0.1",
|
||||
"Newtonsoft.Json": "13.0.1",
|
||||
"System.CodeDom": "4.5.0",
|
||||
"System.Collections.Immutable": "1.5.0",
|
||||
"System.Memory": "4.5.3",
|
||||
"System.Reflection.Emit.Lightweight": "4.3.0",
|
||||
"System.Threading.Channels": "4.7.1"
|
||||
}
|
||||
},
|
||||
"Resourcer.Fody": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.8.1, )",
|
||||
"resolved": "1.8.1",
|
||||
"contentHash": "FPeK4jKyyX5+mIjTnHNReGZk2/2xDhmu44UsBI5w9WEhbr4oTMmht3rnBr46A+GCGepC4+2N41K4vExDYiGNVQ==",
|
||||
"dependencies": {
|
||||
"Fody": "6.6.4"
|
||||
}
|
||||
},
|
||||
"XivCommon": {
|
||||
"type": "Direct",
|
||||
"requested": "[9.0.0, )",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "avaBp3FmSCi/PiQhntCeBDYOHejdyTWmFtz4pRBVQQ8vHkmRx+YTk1la9dkYBMlXxRXKckEdH1iI1Fu61JlE7w=="
|
||||
},
|
||||
"YamlDotNet": {
|
||||
"type": "Direct",
|
||||
"requested": "[13.4.0, )",
|
||||
"resolved": "13.4.0",
|
||||
"contentHash": "XkqTRgpt/tkqX9sOKcdPAZLVMktR5sBFwt7aK/PP061k9yoUntr/Qc8o9KshjSte34WYTIuCrpBs1+LwR7dFdA=="
|
||||
},
|
||||
"Microsoft.ML.CpuMath": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.1",
|
||||
"contentHash": "UjxC9Ju6QPqyvTkj4ggsvnsqKYnbGO1TEypDzKDiIPBGlU/t7Pvx+oV9DDVhZzDKvb34zpLhAm/g79oBuJS3aw=="
|
||||
},
|
||||
"Microsoft.ML.DataView": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.1",
|
||||
"contentHash": "w+GkAXlxaut65Lm+Fbp34YTfp0/9jGRn9uiVlL7Lls0/v+4IJM7SMTHfhvegPU48cyI6K2kzaK9j2Va/labhTA==",
|
||||
"dependencies": {
|
||||
"System.Collections.Immutable": "1.5.0",
|
||||
"System.Memory": "4.5.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETCore.Platforms": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
|
||||
},
|
||||
"Microsoft.NETCore.Targets": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
|
||||
},
|
||||
"System.CodeDom": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "gqpR1EeXOuzNQWL7rOzmtdIz3CaXVjSQCiaGOs2ivjPwynKSJYm39X81fdlp7WuojZs/Z5t1k5ni7HtKQurhjw=="
|
||||
},
|
||||
"System.Collections.Immutable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.5.0",
|
||||
"contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ=="
|
||||
},
|
||||
"System.IO": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"System.Threading.Tasks": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||
},
|
||||
"System.Reflection": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.Emit.ILGeneration": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==",
|
||||
"dependencies": {
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.Emit.Lightweight": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==",
|
||||
"dependencies": {
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.7.1",
|
||||
"contentHash": "6akRtHK/wab3246t4p5v3HQrtQk8LboOt5T4dtpNgsp3zvDeM4/Gx8V12t0h+c/W9/enUrilk8n6EQqdQorZAA=="
|
||||
},
|
||||
"System.Threading.Tasks": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"nosoliciting.interface": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.ML": "[2.0.1, )"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
README.md
Normal file
16
README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# NoSoliciting
|
||||
|
||||
*Adblock for FFXIV.*
|
||||
|
||||
## Summary
|
||||
|
||||
NoSoliciting filters chat messages and Party Finder listings based on
|
||||
various built-in and custom filters. The built-in filters are generated
|
||||
using data from `NoSoliciting.Trainer/data.csv` to create a machine
|
||||
learning model. They can be updated without a plugin update. Custom
|
||||
filters are user-defined and are either case-insensitive substrings or
|
||||
regular expressions.
|
||||
|
||||
All messages and listings filtered can be logged in case of false
|
||||
positives, and there is a reporting mechanism in which users can
|
||||
report if NoSoliciting had a false positive or a false negative.
|
83
icon.svg
Executable file
83
icon.svg
Executable file
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="458.08871"
|
||||
height="458.08871"
|
||||
viewBox="0 0 121.20264 121.20264"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:export-filename="C:\Users\Anna\Desktop\nosol.png"
|
||||
inkscape:export-xdpi="107.298"
|
||||
inkscape:export-ydpi="107.298"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
sodipodi:docname="nosol.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.7039681"
|
||||
inkscape:cx="175.7662"
|
||||
inkscape:cy="230.34468"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1592"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
units="px"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-53.447652,-85.066737)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#af3310;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path33"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="8"
|
||||
sodipodi:cx="-396.65961"
|
||||
sodipodi:cy="345.39066"
|
||||
sodipodi:r1="243.58626"
|
||||
sodipodi:r2="225.04436"
|
||||
sodipodi:arg1="0"
|
||||
sodipodi:arg2="0.39269908"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -153.07335,345.39066 -71.34476,172.24149 -172.2415,71.34476 -172.24149,-71.34476 -71.34476,-172.24149 71.34476,-172.2415 172.24149,-71.34476 172.2415,71.34476 z"
|
||||
transform="matrix(0.24444312,0.10125166,-0.10125166,0.24444312,245.98106,101.40213)"
|
||||
inkscape:transform-center-x="-1.8847157e-06"
|
||||
inkscape:transform-center-y="-3.1424378e-06"
|
||||
inkscape:export-filename="C:\Users\Anna\Desktop\path33.png"
|
||||
inkscape:export-xdpi="61.923672"
|
||||
inkscape:export-ydpi="61.923672" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:38.4628px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
x="57.772713"
|
||||
y="159.98831"
|
||||
id="text3416"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3414"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
x="57.772713"
|
||||
y="159.98831">NoSol</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
Loading…
Reference in New Issue
Block a user