Matrix: switch to the mautrix-go library
Also: add initial support for [Matrix] application services
This commit is contained in:
374
vendor/maunium.net/go/maulogger/v2/LICENSE
generated
vendored
Normal file
374
vendor/maunium.net/go/maulogger/v2/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,374 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
6
vendor/maunium.net/go/maulogger/v2/README.md
generated
vendored
Normal file
6
vendor/maunium.net/go/maulogger/v2/README.md
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# maulogger
|
||||
A logger in Go. Deprecated in favor of [zerolog](https://github.com/rs/zerolog).
|
||||
|
||||
Utilities for migrating gracefully can be found in the maulogadapt package,
|
||||
it includes both wrapping a zerolog in the maulogger interface, and wrapping a
|
||||
maulogger as a zerolog output writer.
|
||||
284
vendor/maunium.net/go/maulogger/v2/defaults.go
generated
vendored
Normal file
284
vendor/maunium.net/go/maulogger/v2/defaults.go
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
// mauLogger - A logger for Go programs
|
||||
// Copyright (c) 2016-2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package maulogger
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// DefaultLogger ...
|
||||
var DefaultLogger = Create().(*BasicLogger)
|
||||
|
||||
// SetWriter formats the given parts with fmt.Sprint and logs the result with the SetWriter level
|
||||
func SetWriter(w *os.File) {
|
||||
DefaultLogger.SetWriter(w)
|
||||
}
|
||||
|
||||
// OpenFile formats the given parts with fmt.Sprint and logs the result with the OpenFile level
|
||||
func OpenFile() error {
|
||||
return DefaultLogger.OpenFile()
|
||||
}
|
||||
|
||||
// Close formats the given parts with fmt.Sprint and logs the result with the Close level
|
||||
func Close() error {
|
||||
return DefaultLogger.Close()
|
||||
}
|
||||
|
||||
// Sub creates a Sublogger
|
||||
func Sub(module string) Logger {
|
||||
return DefaultLogger.Sub(module)
|
||||
}
|
||||
|
||||
// Raw formats the given parts with fmt.Sprint and logs the result with the Raw level
|
||||
func Rawm(level Level, metadata map[string]interface{}, module, message string) {
|
||||
DefaultLogger.Raw(level, metadata, module, message)
|
||||
}
|
||||
|
||||
func Raw(level Level, module, message string) {
|
||||
DefaultLogger.Raw(level, map[string]interface{}{}, module, message)
|
||||
}
|
||||
|
||||
// Log formats the given parts with fmt.Sprint and logs the result with the given level
|
||||
func Log(level Level, parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Log(level, parts...)
|
||||
}
|
||||
|
||||
// Logln formats the given parts with fmt.Sprintln and logs the result with the given level
|
||||
func Logln(level Level, parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Logln(level, parts...)
|
||||
}
|
||||
|
||||
// Logf formats the given message and args with fmt.Sprintf and logs the result with the given level
|
||||
func Logf(level Level, message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Logf(level, message, args...)
|
||||
}
|
||||
|
||||
// Logfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the given level
|
||||
func Logfln(level Level, message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Logfln(level, message, args...)
|
||||
}
|
||||
|
||||
// Debug formats the given parts with fmt.Sprint and logs the result with the Debug level
|
||||
func Debug(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Debug(parts...)
|
||||
}
|
||||
|
||||
// Debugln formats the given parts with fmt.Sprintln and logs the result with the Debug level
|
||||
func Debugln(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Debugln(parts...)
|
||||
}
|
||||
|
||||
// Debugf formats the given message and args with fmt.Sprintf and logs the result with the Debug level
|
||||
func Debugf(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Debugf(message, args...)
|
||||
}
|
||||
|
||||
// Debugfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Debug level
|
||||
func Debugfln(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Debugfln(message, args...)
|
||||
}
|
||||
|
||||
// Info formats the given parts with fmt.Sprint and logs the result with the Info level
|
||||
func Info(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Info(parts...)
|
||||
}
|
||||
|
||||
// Infoln formats the given parts with fmt.Sprintln and logs the result with the Info level
|
||||
func Infoln(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Infoln(parts...)
|
||||
}
|
||||
|
||||
// Infof formats the given message and args with fmt.Sprintf and logs the result with the Info level
|
||||
func Infof(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Infof(message, args...)
|
||||
}
|
||||
|
||||
// Infofln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Info level
|
||||
func Infofln(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Infofln(message, args...)
|
||||
}
|
||||
|
||||
// Warn formats the given parts with fmt.Sprint and logs the result with the Warn level
|
||||
func Warn(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Warn(parts...)
|
||||
}
|
||||
|
||||
// Warnln formats the given parts with fmt.Sprintln and logs the result with the Warn level
|
||||
func Warnln(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Warnln(parts...)
|
||||
}
|
||||
|
||||
// Warnf formats the given message and args with fmt.Sprintf and logs the result with the Warn level
|
||||
func Warnf(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Warnf(message, args...)
|
||||
}
|
||||
|
||||
// Warnfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Warn level
|
||||
func Warnfln(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Warnfln(message, args...)
|
||||
}
|
||||
|
||||
// Error formats the given parts with fmt.Sprint and logs the result with the Error level
|
||||
func Error(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Error(parts...)
|
||||
}
|
||||
|
||||
// Errorln formats the given parts with fmt.Sprintln and logs the result with the Error level
|
||||
func Errorln(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Errorln(parts...)
|
||||
}
|
||||
|
||||
// Errorf formats the given message and args with fmt.Sprintf and logs the result with the Error level
|
||||
func Errorf(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Errorf(message, args...)
|
||||
}
|
||||
|
||||
// Errorfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Error level
|
||||
func Errorfln(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Errorfln(message, args...)
|
||||
}
|
||||
|
||||
// Fatal formats the given parts with fmt.Sprint and logs the result with the Fatal level
|
||||
func Fatal(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Fatal(parts...)
|
||||
}
|
||||
|
||||
// Fatalln formats the given parts with fmt.Sprintln and logs the result with the Fatal level
|
||||
func Fatalln(parts ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Fatalln(parts...)
|
||||
}
|
||||
|
||||
// Fatalf formats the given message and args with fmt.Sprintf and logs the result with the Fatal level
|
||||
func Fatalf(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Fatalf(message, args...)
|
||||
}
|
||||
|
||||
// Fatalfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Fatal level
|
||||
func Fatalfln(message string, args ...interface{}) {
|
||||
DefaultLogger.DefaultSub.Fatalfln(message, args...)
|
||||
}
|
||||
|
||||
// Log formats the given parts with fmt.Sprint and logs the result with the given level
|
||||
func (log *BasicLogger) Log(level Level, parts ...interface{}) {
|
||||
log.DefaultSub.Log(level, parts...)
|
||||
}
|
||||
|
||||
// Logln formats the given parts with fmt.Sprintln and logs the result with the given level
|
||||
func (log *BasicLogger) Logln(level Level, parts ...interface{}) {
|
||||
log.DefaultSub.Logln(level, parts...)
|
||||
}
|
||||
|
||||
// Logf formats the given message and args with fmt.Sprintf and logs the result with the given level
|
||||
func (log *BasicLogger) Logf(level Level, message string, args ...interface{}) {
|
||||
log.DefaultSub.Logf(level, message, args...)
|
||||
}
|
||||
|
||||
// Logfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the given level
|
||||
func (log *BasicLogger) Logfln(level Level, message string, args ...interface{}) {
|
||||
log.DefaultSub.Logfln(level, message, args...)
|
||||
}
|
||||
|
||||
// Debug formats the given parts with fmt.Sprint and logs the result with the Debug level
|
||||
func (log *BasicLogger) Debug(parts ...interface{}) {
|
||||
log.DefaultSub.Debug(parts...)
|
||||
}
|
||||
|
||||
// Debugln formats the given parts with fmt.Sprintln and logs the result with the Debug level
|
||||
func (log *BasicLogger) Debugln(parts ...interface{}) {
|
||||
log.DefaultSub.Debugln(parts...)
|
||||
}
|
||||
|
||||
// Debugf formats the given message and args with fmt.Sprintf and logs the result with the Debug level
|
||||
func (log *BasicLogger) Debugf(message string, args ...interface{}) {
|
||||
log.DefaultSub.Debugf(message, args...)
|
||||
}
|
||||
|
||||
// Debugfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Debug level
|
||||
func (log *BasicLogger) Debugfln(message string, args ...interface{}) {
|
||||
log.DefaultSub.Debugfln(message, args...)
|
||||
}
|
||||
|
||||
// Info formats the given parts with fmt.Sprint and logs the result with the Info level
|
||||
func (log *BasicLogger) Info(parts ...interface{}) {
|
||||
log.DefaultSub.Info(parts...)
|
||||
}
|
||||
|
||||
// Infoln formats the given parts with fmt.Sprintln and logs the result with the Info level
|
||||
func (log *BasicLogger) Infoln(parts ...interface{}) {
|
||||
log.DefaultSub.Infoln(parts...)
|
||||
}
|
||||
|
||||
// Infofln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Info level
|
||||
func (log *BasicLogger) Infofln(message string, args ...interface{}) {
|
||||
log.DefaultSub.Infofln(message, args...)
|
||||
}
|
||||
|
||||
// Infof formats the given message and args with fmt.Sprintf and logs the result with the Info level
|
||||
func (log *BasicLogger) Infof(message string, args ...interface{}) {
|
||||
log.DefaultSub.Infof(message, args...)
|
||||
}
|
||||
|
||||
// Warn formats the given parts with fmt.Sprint and logs the result with the Warn level
|
||||
func (log *BasicLogger) Warn(parts ...interface{}) {
|
||||
log.DefaultSub.Warn(parts...)
|
||||
}
|
||||
|
||||
// Warnln formats the given parts with fmt.Sprintln and logs the result with the Warn level
|
||||
func (log *BasicLogger) Warnln(parts ...interface{}) {
|
||||
log.DefaultSub.Warnln(parts...)
|
||||
}
|
||||
|
||||
// Warnfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Warn level
|
||||
func (log *BasicLogger) Warnfln(message string, args ...interface{}) {
|
||||
log.DefaultSub.Warnfln(message, args...)
|
||||
}
|
||||
|
||||
// Warnf formats the given message and args with fmt.Sprintf and logs the result with the Warn level
|
||||
func (log *BasicLogger) Warnf(message string, args ...interface{}) {
|
||||
log.DefaultSub.Warnf(message, args...)
|
||||
}
|
||||
|
||||
// Error formats the given parts with fmt.Sprint and logs the result with the Error level
|
||||
func (log *BasicLogger) Error(parts ...interface{}) {
|
||||
log.DefaultSub.Error(parts...)
|
||||
}
|
||||
|
||||
// Errorln formats the given parts with fmt.Sprintln and logs the result with the Error level
|
||||
func (log *BasicLogger) Errorln(parts ...interface{}) {
|
||||
log.DefaultSub.Errorln(parts...)
|
||||
}
|
||||
|
||||
// Errorf formats the given message and args with fmt.Sprintf and logs the result with the Error level
|
||||
func (log *BasicLogger) Errorf(message string, args ...interface{}) {
|
||||
log.DefaultSub.Errorf(message, args...)
|
||||
}
|
||||
|
||||
// Errorfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Error level
|
||||
func (log *BasicLogger) Errorfln(message string, args ...interface{}) {
|
||||
log.DefaultSub.Errorfln(message, args...)
|
||||
}
|
||||
|
||||
// Fatal formats the given parts with fmt.Sprint and logs the result with the Fatal level
|
||||
func (log *BasicLogger) Fatal(parts ...interface{}) {
|
||||
log.DefaultSub.Fatal(parts...)
|
||||
}
|
||||
|
||||
// Fatalln formats the given parts with fmt.Sprintln and logs the result with the Fatal level
|
||||
func (log *BasicLogger) Fatalln(parts ...interface{}) {
|
||||
log.DefaultSub.Fatalln(parts...)
|
||||
}
|
||||
|
||||
// Fatalf formats the given message and args with fmt.Sprintf and logs the result with the Fatal level
|
||||
func (log *BasicLogger) Fatalf(message string, args ...interface{}) {
|
||||
log.DefaultSub.Fatalf(message, args...)
|
||||
}
|
||||
|
||||
// Fatalfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Fatal level
|
||||
func (log *BasicLogger) Fatalfln(message string, args ...interface{}) {
|
||||
log.DefaultSub.Fatalfln(message, args...)
|
||||
}
|
||||
47
vendor/maunium.net/go/maulogger/v2/level.go
generated
vendored
Normal file
47
vendor/maunium.net/go/maulogger/v2/level.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// mauLogger - A logger for Go programs
|
||||
// Copyright (c) 2016-2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package maulogger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Level is the severity level of a log entry.
|
||||
type Level struct {
|
||||
Name string
|
||||
Severity, Color int
|
||||
}
|
||||
|
||||
var (
|
||||
// LevelDebug is the level for debug messages.
|
||||
LevelDebug = Level{Name: "DEBUG", Color: -1, Severity: 0}
|
||||
// LevelInfo is the level for basic log messages.
|
||||
LevelInfo = Level{Name: "INFO", Color: 36, Severity: 10}
|
||||
// LevelWarn is the level saying that something went wrong, but the program will continue operating mostly normally.
|
||||
LevelWarn = Level{Name: "WARN", Color: 33, Severity: 50}
|
||||
// LevelError is the level saying that something went wrong and the program may not operate as expected, but will still continue.
|
||||
LevelError = Level{Name: "ERROR", Color: 31, Severity: 100}
|
||||
// LevelFatal is the level saying that something went wrong and the program will not operate normally.
|
||||
LevelFatal = Level{Name: "FATAL", Color: 35, Severity: 9001}
|
||||
)
|
||||
|
||||
// GetColor gets the ANSI escape color code for the log level.
|
||||
func (lvl Level) GetColor() string {
|
||||
if lvl.Color < 0 {
|
||||
return "\x1b[0m"
|
||||
}
|
||||
return fmt.Sprintf("\x1b[%dm", lvl.Color)
|
||||
}
|
||||
|
||||
// GetReset gets the ANSI escape reset code.
|
||||
func (lvl Level) GetReset() string {
|
||||
if lvl.Color < 0 {
|
||||
return ""
|
||||
}
|
||||
return "\x1b[0m"
|
||||
}
|
||||
224
vendor/maunium.net/go/maulogger/v2/logger.go
generated
vendored
Normal file
224
vendor/maunium.net/go/maulogger/v2/logger.go
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
// mauLogger - A logger for Go programs
|
||||
// Copyright (c) 2016-2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package maulogger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LoggerFileFormat ...
|
||||
type LoggerFileFormat func(now string, i int) string
|
||||
|
||||
type BasicLogger struct {
|
||||
PrintLevel int
|
||||
FlushLineThreshold int
|
||||
FileTimeFormat string
|
||||
FileFormat LoggerFileFormat
|
||||
TimeFormat string
|
||||
FileMode os.FileMode
|
||||
DefaultSub Logger
|
||||
|
||||
JSONFile bool
|
||||
JSONStdout bool
|
||||
|
||||
stdoutEncoder *json.Encoder
|
||||
fileEncoder *json.Encoder
|
||||
|
||||
writer *os.File
|
||||
writerLock sync.Mutex
|
||||
StdoutLock sync.Mutex
|
||||
StderrLock sync.Mutex
|
||||
lines int
|
||||
|
||||
metadata map[string]interface{}
|
||||
}
|
||||
|
||||
// Logger contains advanced logging functions
|
||||
type Logger interface {
|
||||
Sub(module string) Logger
|
||||
Subm(module string, metadata map[string]interface{}) Logger
|
||||
WithDefaultLevel(level Level) Logger
|
||||
GetParent() Logger
|
||||
|
||||
Writer(level Level) io.WriteCloser
|
||||
|
||||
Log(level Level, parts ...interface{})
|
||||
Logln(level Level, parts ...interface{})
|
||||
Logf(level Level, message string, args ...interface{})
|
||||
Logfln(level Level, message string, args ...interface{})
|
||||
|
||||
Debug(parts ...interface{})
|
||||
Debugln(parts ...interface{})
|
||||
Debugf(message string, args ...interface{})
|
||||
Debugfln(message string, args ...interface{})
|
||||
Info(parts ...interface{})
|
||||
Infoln(parts ...interface{})
|
||||
Infof(message string, args ...interface{})
|
||||
Infofln(message string, args ...interface{})
|
||||
Warn(parts ...interface{})
|
||||
Warnln(parts ...interface{})
|
||||
Warnf(message string, args ...interface{})
|
||||
Warnfln(message string, args ...interface{})
|
||||
Error(parts ...interface{})
|
||||
Errorln(parts ...interface{})
|
||||
Errorf(message string, args ...interface{})
|
||||
Errorfln(message string, args ...interface{})
|
||||
Fatal(parts ...interface{})
|
||||
Fatalln(parts ...interface{})
|
||||
Fatalf(message string, args ...interface{})
|
||||
Fatalfln(message string, args ...interface{})
|
||||
}
|
||||
|
||||
// Create a Logger
|
||||
func Createm(metadata map[string]interface{}) Logger {
|
||||
var log = &BasicLogger{
|
||||
PrintLevel: 10,
|
||||
FileTimeFormat: "2006-01-02",
|
||||
FileFormat: func(now string, i int) string { return fmt.Sprintf("%[1]s-%02[2]d.log", now, i) },
|
||||
TimeFormat: "15:04:05 02.01.2006",
|
||||
FileMode: 0600,
|
||||
FlushLineThreshold: 5,
|
||||
lines: 0,
|
||||
metadata: metadata,
|
||||
}
|
||||
log.DefaultSub = log.Sub("")
|
||||
return log
|
||||
}
|
||||
|
||||
func Create() Logger {
|
||||
return Createm(map[string]interface{}{})
|
||||
}
|
||||
|
||||
func (log *BasicLogger) EnableJSONStdout() {
|
||||
log.JSONStdout = true
|
||||
log.stdoutEncoder = json.NewEncoder(os.Stdout)
|
||||
}
|
||||
|
||||
func (log *BasicLogger) GetParent() Logger {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriter formats the given parts with fmt.Sprint and logs the result with the SetWriter level
|
||||
func (log *BasicLogger) SetWriter(w *os.File) {
|
||||
log.writer = w
|
||||
if log.JSONFile {
|
||||
log.fileEncoder = json.NewEncoder(w)
|
||||
}
|
||||
}
|
||||
|
||||
// OpenFile formats the given parts with fmt.Sprint and logs the result with the OpenFile level
|
||||
func (log *BasicLogger) OpenFile() error {
|
||||
now := time.Now().Format(log.FileTimeFormat)
|
||||
i := 1
|
||||
for ; ; i++ {
|
||||
if _, err := os.Stat(log.FileFormat(now, i)); os.IsNotExist(err) {
|
||||
break
|
||||
}
|
||||
}
|
||||
writer, err := os.OpenFile(log.FileFormat(now, i), os.O_WRONLY|os.O_CREATE|os.O_APPEND, log.FileMode)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if writer == nil {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
log.SetWriter(writer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close formats the given parts with fmt.Sprint and logs the result with the Close level
|
||||
func (log *BasicLogger) Close() error {
|
||||
if log.writer != nil {
|
||||
return log.writer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type logLine struct {
|
||||
log *BasicLogger
|
||||
|
||||
Command string `json:"command"`
|
||||
Time time.Time `json:"time"`
|
||||
Level string `json:"level"`
|
||||
Module string `json:"module"`
|
||||
Message string `json:"message"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
}
|
||||
|
||||
func (ll logLine) String() string {
|
||||
if len(ll.Module) == 0 {
|
||||
return fmt.Sprintf("[%s] [%s] %s", ll.Time.Format(ll.log.TimeFormat), ll.Level, ll.Message)
|
||||
} else {
|
||||
return fmt.Sprintf("[%s] [%s/%s] %s", ll.Time.Format(ll.log.TimeFormat), ll.Module, ll.Level, ll.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func reduceItem(m1, m2 map[string]interface{}) map[string]interface{} {
|
||||
m3 := map[string]interface{}{}
|
||||
|
||||
_merge := func(m map[string]interface{}) {
|
||||
for ia, va := range m {
|
||||
m3[ia] = va
|
||||
}
|
||||
}
|
||||
|
||||
_merge(m1)
|
||||
_merge(m2)
|
||||
|
||||
return m3
|
||||
}
|
||||
|
||||
// Raw formats the given parts with fmt.Sprint and logs the result with the Raw level
|
||||
func (log *BasicLogger) Raw(level Level, extraMetadata map[string]interface{}, module, origMessage string) {
|
||||
message := logLine{log, "log", time.Now(), level.Name, module, strings.TrimSpace(origMessage), reduceItem(log.metadata, extraMetadata)}
|
||||
|
||||
if log.writer != nil {
|
||||
log.writerLock.Lock()
|
||||
var err error
|
||||
if log.JSONFile {
|
||||
err = log.fileEncoder.Encode(&message)
|
||||
} else {
|
||||
_, err = log.writer.WriteString(message.String())
|
||||
_, _ = log.writer.WriteString("\n")
|
||||
}
|
||||
log.writerLock.Unlock()
|
||||
if err != nil {
|
||||
log.StderrLock.Lock()
|
||||
_, _ = os.Stderr.WriteString("Failed to write to log file:")
|
||||
_, _ = os.Stderr.WriteString(err.Error())
|
||||
log.StderrLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
if level.Severity >= log.PrintLevel {
|
||||
if log.JSONStdout {
|
||||
log.StdoutLock.Lock()
|
||||
_ = log.stdoutEncoder.Encode(&message)
|
||||
log.StdoutLock.Unlock()
|
||||
} else if level.Severity >= LevelError.Severity {
|
||||
log.StderrLock.Lock()
|
||||
_, _ = os.Stderr.WriteString(level.GetColor())
|
||||
_, _ = os.Stderr.WriteString(message.String())
|
||||
_, _ = os.Stderr.WriteString(level.GetReset())
|
||||
_, _ = os.Stderr.WriteString("\n")
|
||||
log.StderrLock.Unlock()
|
||||
} else {
|
||||
log.StdoutLock.Lock()
|
||||
_, _ = os.Stdout.WriteString(level.GetColor())
|
||||
_, _ = os.Stdout.WriteString(message.String())
|
||||
_, _ = os.Stdout.WriteString(level.GetReset())
|
||||
_, _ = os.Stdout.WriteString("\n")
|
||||
log.StdoutLock.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
185
vendor/maunium.net/go/maulogger/v2/maulogadapt/mauzerolog.go
generated
vendored
Normal file
185
vendor/maunium.net/go/maulogger/v2/maulogadapt/mauzerolog.go
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package maulogadapt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"maunium.net/go/maulogger/v2"
|
||||
)
|
||||
|
||||
type MauZeroLog struct {
|
||||
*zerolog.Logger
|
||||
orig *zerolog.Logger
|
||||
mod string
|
||||
}
|
||||
|
||||
func ZeroAsMau(log *zerolog.Logger) maulogger.Logger {
|
||||
return MauZeroLog{log, log, ""}
|
||||
}
|
||||
|
||||
var _ maulogger.Logger = (*MauZeroLog)(nil)
|
||||
|
||||
func (m MauZeroLog) Sub(module string) maulogger.Logger {
|
||||
return m.Subm(module, map[string]interface{}{})
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Subm(module string, metadata map[string]interface{}) maulogger.Logger {
|
||||
if m.mod != "" {
|
||||
module = fmt.Sprintf("%s/%s", m.mod, module)
|
||||
}
|
||||
var orig zerolog.Logger
|
||||
if m.orig != nil {
|
||||
orig = *m.orig
|
||||
} else {
|
||||
orig = *m.Logger
|
||||
}
|
||||
if len(metadata) > 0 {
|
||||
with := m.orig.With()
|
||||
for key, value := range metadata {
|
||||
with = with.Interface(key, value)
|
||||
}
|
||||
orig = with.Logger()
|
||||
}
|
||||
log := orig.With().Str("module", module).Logger()
|
||||
return MauZeroLog{&log, &orig, module}
|
||||
}
|
||||
|
||||
func (m MauZeroLog) WithDefaultLevel(_ maulogger.Level) maulogger.Logger {
|
||||
return m
|
||||
}
|
||||
|
||||
func (m MauZeroLog) GetParent() maulogger.Logger {
|
||||
return nil
|
||||
}
|
||||
|
||||
type nopWriteCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopWriteCloser) Close() error { return nil }
|
||||
|
||||
func (m MauZeroLog) Writer(level maulogger.Level) io.WriteCloser {
|
||||
return nopWriteCloser{m.Logger.With().Str(zerolog.LevelFieldName, zerolog.LevelFieldMarshalFunc(mauToZeroLevel(level))).Logger()}
|
||||
}
|
||||
|
||||
func mauToZeroLevel(level maulogger.Level) zerolog.Level {
|
||||
switch level {
|
||||
case maulogger.LevelDebug:
|
||||
return zerolog.DebugLevel
|
||||
case maulogger.LevelInfo:
|
||||
return zerolog.InfoLevel
|
||||
case maulogger.LevelWarn:
|
||||
return zerolog.WarnLevel
|
||||
case maulogger.LevelError:
|
||||
return zerolog.ErrorLevel
|
||||
case maulogger.LevelFatal:
|
||||
return zerolog.FatalLevel
|
||||
default:
|
||||
return zerolog.TraceLevel
|
||||
}
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Log(level maulogger.Level, parts ...interface{}) {
|
||||
m.Logger.WithLevel(mauToZeroLevel(level)).Msg(fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Logln(level maulogger.Level, parts ...interface{}) {
|
||||
m.Logger.WithLevel(mauToZeroLevel(level)).Msg(strings.TrimSuffix(fmt.Sprintln(parts...), "\n"))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Logf(level maulogger.Level, message string, args ...interface{}) {
|
||||
m.Logger.WithLevel(mauToZeroLevel(level)).Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Logfln(level maulogger.Level, message string, args ...interface{}) {
|
||||
m.Logger.WithLevel(mauToZeroLevel(level)).Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Debug(parts ...interface{}) {
|
||||
m.Logger.Debug().Msg(fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Debugln(parts ...interface{}) {
|
||||
m.Logger.Debug().Msg(strings.TrimSuffix(fmt.Sprintln(parts...), "\n"))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Debugf(message string, args ...interface{}) {
|
||||
m.Logger.Debug().Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Debugfln(message string, args ...interface{}) {
|
||||
m.Logger.Debug().Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Info(parts ...interface{}) {
|
||||
m.Logger.Info().Msg(fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Infoln(parts ...interface{}) {
|
||||
m.Logger.Info().Msg(strings.TrimSuffix(fmt.Sprintln(parts...), "\n"))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Infof(message string, args ...interface{}) {
|
||||
m.Logger.Info().Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Infofln(message string, args ...interface{}) {
|
||||
m.Logger.Info().Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Warn(parts ...interface{}) {
|
||||
m.Logger.Warn().Msg(fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Warnln(parts ...interface{}) {
|
||||
m.Logger.Warn().Msg(strings.TrimSuffix(fmt.Sprintln(parts...), "\n"))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Warnf(message string, args ...interface{}) {
|
||||
m.Logger.Warn().Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Warnfln(message string, args ...interface{}) {
|
||||
m.Logger.Warn().Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Error(parts ...interface{}) {
|
||||
m.Logger.Error().Msg(fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Errorln(parts ...interface{}) {
|
||||
m.Logger.Error().Msg(strings.TrimSuffix(fmt.Sprintln(parts...), "\n"))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Errorf(message string, args ...interface{}) {
|
||||
m.Logger.Error().Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Errorfln(message string, args ...interface{}) {
|
||||
m.Logger.Error().Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Fatal(parts ...interface{}) {
|
||||
m.Logger.WithLevel(zerolog.FatalLevel).Msg(fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Fatalln(parts ...interface{}) {
|
||||
m.Logger.WithLevel(zerolog.FatalLevel).Msg(strings.TrimSuffix(fmt.Sprintln(parts...), "\n"))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Fatalf(message string, args ...interface{}) {
|
||||
m.Logger.WithLevel(zerolog.FatalLevel).Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
func (m MauZeroLog) Fatalfln(message string, args ...interface{}) {
|
||||
m.Logger.WithLevel(zerolog.FatalLevel).Msg(fmt.Sprintf(message, args...))
|
||||
}
|
||||
73
vendor/maunium.net/go/maulogger/v2/maulogadapt/zeromaulog.go
generated
vendored
Normal file
73
vendor/maunium.net/go/maulogger/v2/maulogadapt/zeromaulog.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package maulogadapt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
|
||||
"maunium.net/go/maulogger/v2"
|
||||
)
|
||||
|
||||
// ZeroMauLog is a simple wrapper for a maulogger that can be set as the output writer for zerolog.
|
||||
type ZeroMauLog struct {
|
||||
maulogger.Logger
|
||||
}
|
||||
|
||||
func MauAsZero(log maulogger.Logger) *zerolog.Logger {
|
||||
zero := zerolog.New(&ZeroMauLog{log})
|
||||
return &zero
|
||||
}
|
||||
|
||||
var _ zerolog.LevelWriter = (*ZeroMauLog)(nil)
|
||||
|
||||
func (z *ZeroMauLog) Write(p []byte) (n int, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (z *ZeroMauLog) WriteLevel(level zerolog.Level, p []byte) (n int, err error) {
|
||||
var mauLevel maulogger.Level
|
||||
switch level {
|
||||
case zerolog.DebugLevel:
|
||||
mauLevel = maulogger.LevelDebug
|
||||
case zerolog.InfoLevel, zerolog.NoLevel:
|
||||
mauLevel = maulogger.LevelInfo
|
||||
case zerolog.WarnLevel:
|
||||
mauLevel = maulogger.LevelWarn
|
||||
case zerolog.ErrorLevel:
|
||||
mauLevel = maulogger.LevelError
|
||||
case zerolog.FatalLevel, zerolog.PanicLevel:
|
||||
mauLevel = maulogger.LevelFatal
|
||||
case zerolog.Disabled, zerolog.TraceLevel:
|
||||
fallthrough
|
||||
default:
|
||||
return 0, nil
|
||||
}
|
||||
p = bytes.TrimSuffix(p, []byte{'\n'})
|
||||
msg := gjson.GetBytes(p, zerolog.MessageFieldName).Str
|
||||
|
||||
p, err = sjson.DeleteBytes(p, zerolog.MessageFieldName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p, err = sjson.DeleteBytes(p, zerolog.LevelFieldName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p, err = sjson.DeleteBytes(p, zerolog.TimestampFieldName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(p) > 2 {
|
||||
msg += " " + string(p)
|
||||
}
|
||||
z.Log(mauLevel, msg)
|
||||
return len(p), nil
|
||||
}
|
||||
216
vendor/maunium.net/go/maulogger/v2/sublogger.go
generated
vendored
Normal file
216
vendor/maunium.net/go/maulogger/v2/sublogger.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
// mauLogger - A logger for Go programs
|
||||
// Copyright (c) 2016-2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package maulogger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Sublogger struct {
|
||||
topLevel *BasicLogger
|
||||
parent Logger
|
||||
Module string
|
||||
DefaultLevel Level
|
||||
metadata map[string]interface{}
|
||||
}
|
||||
|
||||
// Subm creates a Sublogger
|
||||
func (log *BasicLogger) Subm(module string, metadata map[string]interface{}) Logger {
|
||||
return &Sublogger{
|
||||
topLevel: log,
|
||||
parent: log,
|
||||
Module: module,
|
||||
DefaultLevel: LevelInfo,
|
||||
metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func (log *BasicLogger) Sub(module string) Logger {
|
||||
return log.Subm(module, map[string]interface{}{})
|
||||
}
|
||||
|
||||
// WithDefaultLevel creates a Sublogger with the same Module but different DefaultLevel
|
||||
func (log *BasicLogger) WithDefaultLevel(lvl Level) Logger {
|
||||
return log.DefaultSub.WithDefaultLevel(lvl)
|
||||
}
|
||||
|
||||
func (log *Sublogger) GetParent() Logger {
|
||||
return log.parent
|
||||
}
|
||||
|
||||
// Sub creates a Sublogger
|
||||
func (log *Sublogger) Subm(module string, metadata map[string]interface{}) Logger {
|
||||
if len(module) > 0 {
|
||||
module = fmt.Sprintf("%s/%s", log.Module, module)
|
||||
} else {
|
||||
module = log.Module
|
||||
}
|
||||
|
||||
return &Sublogger{
|
||||
topLevel: log.topLevel,
|
||||
parent: log,
|
||||
Module: module,
|
||||
DefaultLevel: log.DefaultLevel,
|
||||
metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func (log *Sublogger) Sub(module string) Logger {
|
||||
return log.Subm(module, map[string]interface{}{})
|
||||
}
|
||||
|
||||
// WithDefaultLevel creates a Sublogger with the same Module but different DefaultLevel
|
||||
func (log *Sublogger) WithDefaultLevel(lvl Level) Logger {
|
||||
return &Sublogger{
|
||||
topLevel: log.topLevel,
|
||||
parent: log.parent,
|
||||
Module: log.Module,
|
||||
DefaultLevel: lvl,
|
||||
}
|
||||
}
|
||||
|
||||
// SetModule changes the module name of this Sublogger
|
||||
func (log *Sublogger) SetModule(mod string) {
|
||||
log.Module = mod
|
||||
}
|
||||
|
||||
// SetDefaultLevel changes the default logging level of this Sublogger
|
||||
func (log *Sublogger) SetDefaultLevel(lvl Level) {
|
||||
log.DefaultLevel = lvl
|
||||
}
|
||||
|
||||
// SetParent changes the parent of this Sublogger
|
||||
func (log *Sublogger) SetParent(parent *BasicLogger) {
|
||||
log.topLevel = parent
|
||||
}
|
||||
|
||||
//Write ...
|
||||
func (log *Sublogger) Write(p []byte) (n int, err error) {
|
||||
log.topLevel.Raw(log.DefaultLevel, log.metadata, log.Module, string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Log formats the given parts with fmt.Sprint and logs the result with the given level
|
||||
func (log *Sublogger) Log(level Level, parts ...interface{}) {
|
||||
log.topLevel.Raw(level, log.metadata, log.Module, fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
// Logln formats the given parts with fmt.Sprintln and logs the result with the given level
|
||||
func (log *Sublogger) Logln(level Level, parts ...interface{}) {
|
||||
log.topLevel.Raw(level, log.metadata, log.Module, fmt.Sprintln(parts...))
|
||||
}
|
||||
|
||||
// Logf formats the given message and args with fmt.Sprintf and logs the result with the given level
|
||||
func (log *Sublogger) Logf(level Level, message string, args ...interface{}) {
|
||||
log.topLevel.Raw(level, log.metadata, log.Module, fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
// Logfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the given level
|
||||
func (log *Sublogger) Logfln(level Level, message string, args ...interface{}) {
|
||||
log.topLevel.Raw(level, log.metadata, log.Module, fmt.Sprintf(message+"\n", args...))
|
||||
}
|
||||
|
||||
// Debug formats the given parts with fmt.Sprint and logs the result with the Debug level
|
||||
func (log *Sublogger) Debug(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelDebug, log.metadata, log.Module, fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
// Debugln formats the given parts with fmt.Sprintln and logs the result with the Debug level
|
||||
func (log *Sublogger) Debugln(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelDebug, log.metadata, log.Module, fmt.Sprintln(parts...))
|
||||
}
|
||||
|
||||
// Debugf formats the given message and args with fmt.Sprintf and logs the result with the Debug level
|
||||
func (log *Sublogger) Debugf(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelDebug, log.metadata, log.Module, fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
// Debugfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Debug level
|
||||
func (log *Sublogger) Debugfln(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelDebug, log.metadata, log.Module, fmt.Sprintf(message+"\n", args...))
|
||||
}
|
||||
|
||||
// Info formats the given parts with fmt.Sprint and logs the result with the Info level
|
||||
func (log *Sublogger) Info(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelInfo, log.metadata, log.Module, fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
// Infoln formats the given parts with fmt.Sprintln and logs the result with the Info level
|
||||
func (log *Sublogger) Infoln(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelInfo, log.metadata, log.Module, fmt.Sprintln(parts...))
|
||||
}
|
||||
|
||||
// Infof formats the given message and args with fmt.Sprintf and logs the result with the Info level
|
||||
func (log *Sublogger) Infof(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelInfo, log.metadata, log.Module, fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
// Infofln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Info level
|
||||
func (log *Sublogger) Infofln(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelInfo, log.metadata, log.Module, fmt.Sprintf(message+"\n", args...))
|
||||
}
|
||||
|
||||
// Warn formats the given parts with fmt.Sprint and logs the result with the Warn level
|
||||
func (log *Sublogger) Warn(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelWarn, log.metadata, log.Module, fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
// Warnln formats the given parts with fmt.Sprintln and logs the result with the Warn level
|
||||
func (log *Sublogger) Warnln(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelWarn, log.metadata, log.Module, fmt.Sprintln(parts...))
|
||||
}
|
||||
|
||||
// Warnf formats the given message and args with fmt.Sprintf and logs the result with the Warn level
|
||||
func (log *Sublogger) Warnf(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelWarn, log.metadata, log.Module, fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
// Warnfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Warn level
|
||||
func (log *Sublogger) Warnfln(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelWarn, log.metadata, log.Module, fmt.Sprintf(message+"\n", args...))
|
||||
}
|
||||
|
||||
// Error formats the given parts with fmt.Sprint and logs the result with the Error level
|
||||
func (log *Sublogger) Error(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelError, log.metadata, log.Module, fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
// Errorln formats the given parts with fmt.Sprintln and logs the result with the Error level
|
||||
func (log *Sublogger) Errorln(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelError, log.metadata, log.Module, fmt.Sprintln(parts...))
|
||||
}
|
||||
|
||||
// Errorf formats the given message and args with fmt.Sprintf and logs the result with the Error level
|
||||
func (log *Sublogger) Errorf(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelError, log.metadata, log.Module, fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
// Errorfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Error level
|
||||
func (log *Sublogger) Errorfln(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelError, log.metadata, log.Module, fmt.Sprintf(message+"\n", args...))
|
||||
}
|
||||
|
||||
// Fatal formats the given parts with fmt.Sprint and logs the result with the Fatal level
|
||||
func (log *Sublogger) Fatal(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelFatal, log.metadata, log.Module, fmt.Sprint(parts...))
|
||||
}
|
||||
|
||||
// Fatalln formats the given parts with fmt.Sprintln and logs the result with the Fatal level
|
||||
func (log *Sublogger) Fatalln(parts ...interface{}) {
|
||||
log.topLevel.Raw(LevelFatal, log.metadata, log.Module, fmt.Sprintln(parts...))
|
||||
}
|
||||
|
||||
// Fatalf formats the given message and args with fmt.Sprintf and logs the result with the Fatal level
|
||||
func (log *Sublogger) Fatalf(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelFatal, log.metadata, log.Module, fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
// Fatalfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Fatal level
|
||||
func (log *Sublogger) Fatalfln(message string, args ...interface{}) {
|
||||
log.topLevel.Raw(LevelFatal, log.metadata, log.Module, fmt.Sprintf(message+"\n", args...))
|
||||
}
|
||||
78
vendor/maunium.net/go/maulogger/v2/writer.go
generated
vendored
Normal file
78
vendor/maunium.net/go/maulogger/v2/writer.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// mauLogger - A logger for Go programs
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package maulogger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// LogWriter is a buffered io.Writer that writes lines to a Logger.
|
||||
type LogWriter struct {
|
||||
log Logger
|
||||
lock sync.Mutex
|
||||
level Level
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func (log *BasicLogger) Writer(level Level) io.WriteCloser {
|
||||
return &LogWriter{
|
||||
log: log,
|
||||
level: level,
|
||||
}
|
||||
}
|
||||
|
||||
func (log *Sublogger) Writer(level Level) io.WriteCloser {
|
||||
return &LogWriter{
|
||||
log: log,
|
||||
level: level,
|
||||
}
|
||||
}
|
||||
|
||||
func (lw *LogWriter) writeLine(data []byte) {
|
||||
if lw.buf.Len() == 0 {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
lw.log.Logln(lw.level, string(data))
|
||||
} else {
|
||||
lw.buf.Write(data)
|
||||
lw.log.Logln(lw.level, lw.buf.String())
|
||||
lw.buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
// Write will write lines from the given data to the buffer. If the data doesn't end with a line break,
|
||||
// everything after the last line break will be buffered until the next Write or Close call.
|
||||
func (lw *LogWriter) Write(data []byte) (int, error) {
|
||||
lw.lock.Lock()
|
||||
newline := bytes.IndexByte(data, '\n')
|
||||
if newline == len(data)-1 {
|
||||
lw.writeLine(data[:len(data)-1])
|
||||
} else if newline < 0 {
|
||||
lw.buf.Write(data)
|
||||
} else {
|
||||
lines := bytes.Split(data, []byte("\n"))
|
||||
for _, line := range lines[:len(lines)-1] {
|
||||
lw.writeLine(line)
|
||||
}
|
||||
lw.buf.Write(lines[len(lines)-1])
|
||||
}
|
||||
lw.lock.Unlock()
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// Close will flush remaining data in the buffer into the logger.
|
||||
func (lw *LogWriter) Close() error {
|
||||
lw.lock.Lock()
|
||||
lw.log.Logln(lw.level, lw.buf.String())
|
||||
lw.buf.Reset()
|
||||
lw.lock.Unlock()
|
||||
return nil
|
||||
}
|
||||
12
vendor/maunium.net/go/mautrix/.editorconfig
generated
vendored
Normal file
12
vendor/maunium.net/go/mautrix/.editorconfig
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{yaml,yml}]
|
||||
indent_style = space
|
||||
4
vendor/maunium.net/go/mautrix/.gitignore
generated
vendored
Normal file
4
vendor/maunium.net/go/mautrix/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.idea/
|
||||
.vscode/
|
||||
*.db
|
||||
*.log
|
||||
15
vendor/maunium.net/go/mautrix/.pre-commit-config.yaml
generated
vendored
Normal file
15
vendor/maunium.net/go/mautrix/.pre-commit-config.yaml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
exclude_types: [markdown]
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
|
||||
- repo: https://github.com/tekwizely/pre-commit-golang
|
||||
rev: v1.0.0-rc.1
|
||||
hooks:
|
||||
- id: go-imports-repo
|
||||
- id: go-vet-repo-mod
|
||||
612
vendor/maunium.net/go/mautrix/CHANGELOG.md
generated
vendored
Normal file
612
vendor/maunium.net/go/mautrix/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,612 @@
|
||||
## v0.15.0 (2023-03-16)
|
||||
|
||||
### beta.3 (2023-03-15)
|
||||
|
||||
* **Breaking change *(appservice)*** Removed `Load()` and `AppService.Init()`
|
||||
functions. The struct should just be created with `Create()` and the relevant
|
||||
fields should be filled manually.
|
||||
* **Breaking change *(appservice)*** Removed public `HomeserverURL` field and
|
||||
replaced it with a `SetHomeserverURL` method.
|
||||
* *(appservice)* Added support for unix sockets for homeserver URL and
|
||||
appservice HTTP server.
|
||||
* *(client)* Changed request logging to log durations as floats instead of
|
||||
strings (using zerolog's `Dur()`, so the exact output can be configured).
|
||||
* *(bridge)* Changed zerolog to use nanosecond precision timestamps.
|
||||
* *(crypto)* Added message index to log after encrypting/decrypting megolm
|
||||
events, and when failing to decrypt due to duplicate index.
|
||||
* *(sqlstatestore)* Fixed warning log for rooms that don't have encryption
|
||||
enabled.
|
||||
|
||||
### beta.2 (2023-03-02)
|
||||
|
||||
* *(bridge)* Fixed building with `nocrypto` tag.
|
||||
* *(bridge)* Fixed legacy logging config migration not disabling file writer
|
||||
when `file_name_format` was empty.
|
||||
* *(bridge)* Added option to require room power level to run commands.
|
||||
* *(event)* Added structs for [MSC3952]: Intentional Mentions.
|
||||
* *(util/variationselector)* Added `FullyQualify` method to add necessary emoji
|
||||
variation selectors without adding all possible ones.
|
||||
|
||||
[MSC3952]: https://github.com/matrix-org/matrix-spec-proposals/pull/3952
|
||||
|
||||
### beta.1 (2023-02-24)
|
||||
|
||||
* Bumped minimum Go version to 1.19.
|
||||
* **Breaking changes**
|
||||
* *(all)* Switched to zerolog for logging.
|
||||
* The `Client` and `Bridge` structs still include a legacy logger for
|
||||
backwards compatibility.
|
||||
* *(client, appservice)* Moved `SQLStateStore` from appservice module to the
|
||||
top-level (client) module.
|
||||
* *(client, appservice)* Removed unused `Typing` map in `SQLStateStore`.
|
||||
* *(client)* Removed unused `SaveRoom` and `LoadRoom` methods in `Storer`.
|
||||
* *(client, appservice)* Removed deprecated `SendVideo` and `SendImage` methods.
|
||||
* *(client)* Replaced `AppServiceUserID` field with `SetAppServiceUserID` boolean.
|
||||
The `UserID` field is used as the value for the query param.
|
||||
* *(crypto)* Renamed `GobStore` to `MemoryStore` and removed the file saving
|
||||
features. The data can still be persisted, but the persistence part must be
|
||||
implemented separately.
|
||||
* *(crypto)* Removed deprecated `DeviceIdentity` alias
|
||||
(renamed to `id.Device` long ago).
|
||||
* *(client)* Removed `Stringifable` interface as it's the same as `fmt.Stringer`.
|
||||
* *(client)* Renamed `Storer` interface to `SyncStore`. A type alias exists for
|
||||
backwards-compatibility.
|
||||
* *(crypto/cryptohelper)* Added package for a simplified crypto interface for clients.
|
||||
* *(example)* Added e2ee support to example using crypto helper.
|
||||
* *(client)* Changed default syncer to stop syncing on `M_UNKNOWN_TOKEN` errors.
|
||||
|
||||
## v0.14.0 (2023-02-16)
|
||||
|
||||
* **Breaking change *(format)*** Refactored the HTML parser `Context` to have
|
||||
more data.
|
||||
* *(id)* Fixed escaping path components when forming matrix.to URLs
|
||||
or `matrix:` URIs.
|
||||
* *(bridge)* Bumped default timeouts for decrypting incoming messages.
|
||||
* *(bridge)* Added `RawArgs` to commands to allow accessing non-split input.
|
||||
* *(bridge)* Added `ReplyAdvanced` to commands to allow setting markdown
|
||||
settings.
|
||||
* *(event)* Added `notifications` key to `PowerLevelEventContent`.
|
||||
* *(event)* Changed `SetEdit` to cut off edit fallback if the message is long.
|
||||
* *(util)* Added `SyncMap` as a simple generic wrapper for a map with a mutex.
|
||||
* *(util)* Added `ReturnableOnce` as a wrapper for `sync.Once` with a return
|
||||
value.
|
||||
|
||||
## v0.13.0 (2023-01-16)
|
||||
|
||||
* **Breaking change:** Removed `IsTyping` and `SetTyping` in `appservice.StateStore`
|
||||
and removed the `TypingStateStore` struct implementing those methods.
|
||||
* **Breaking change:** Removed legacy fields in Beeper MSS events.
|
||||
* Added knocked rooms to sync response structs.
|
||||
* Added wrapper for `/timestamp_to_event` endpoint added in Matrix v1.6.
|
||||
* Fixed MSC3870 uploads not failing properly after using up the max retry count.
|
||||
* Fixed parsing non-positive ordered list start positions in HTML parser.
|
||||
|
||||
## v0.12.4 (2022-12-16)
|
||||
|
||||
* Added `SendReceipt` to support private read receipts and thread receipts in
|
||||
the same function. `MarkReadWithContent` is now deprecated.
|
||||
* Changed media download methods to return errors if the server returns a
|
||||
non-2xx status code.
|
||||
* Removed legacy `sql_store_upgrade.Upgrade` method. Using `store.DB.Upgrade()`
|
||||
after `NewSQLCryptoStore(...)` is recommended instead (the bridge module does
|
||||
this automatically).
|
||||
* Added missing `suggested` field to `m.space.child` content struct.
|
||||
* Added `device_unused_fallback_key_types` to `/sync` response and appservice
|
||||
transaction structs.
|
||||
* Changed `ReqSetReadMarkers` to omit empty fields.
|
||||
* Changed bridge configs to force `sqlite3-fk-wal` instead of `sqlite3`.
|
||||
* Updated bridge helper to close database connection when stopping.
|
||||
* Fixed read receipt and account data endpoints sending `null` instead of an
|
||||
empty object as the body when content isn't provided.
|
||||
|
||||
## v0.12.3 (2022-11-16)
|
||||
|
||||
* **Breaking change:** Added logging for row iteration in the dbutil package.
|
||||
This changes the return type of `Query` methods from `*sql.Rows` to a new
|
||||
`dbutil.Rows` interface.
|
||||
* Added flag to disable wrapping database upgrades in a transaction (e.g. to
|
||||
allow setting `PRAGMA`s for advanced table mutations on SQLite).
|
||||
* Deprecated `MessageEventContent.GetReplyTo` in favor of directly using
|
||||
`RelatesTo.GetReplyTo`. RelatesTo methods are nil-safe, so checking if
|
||||
RelatesTo is nil is not necessary for using those methods.
|
||||
* Added wrapper for space hierarchyendpoint (thanks to [@mgcm] in [#100]).
|
||||
* Added bridge config option to handle transactions asynchronously.
|
||||
* Added separate channels for to-device events in appservice transaction
|
||||
handler to avoid blocking to-device events behind normal events.
|
||||
* Added `RelatesTo.GetNonFallbackReplyTo` utility method to get the reply event
|
||||
ID, unless the reply is a thread fallback.
|
||||
* Added `event.TextToHTML` as an utility method to HTML-escape a string and
|
||||
replace newlines with `<br/>`.
|
||||
* Added check to bridge encryption helper to make sure the e2ee keys are still
|
||||
on the server. Synapse is known to sometimes lose keys randomly.
|
||||
* Changed bridge crypto syncer to crash on `M_UNKNOWN_TOKEN` errors instead of
|
||||
retrying forever pointlessly.
|
||||
* Fixed verifying signatures of fallback one-time keys.
|
||||
|
||||
[@mgcm]: https://github.com/mgcm
|
||||
[#100]: https://github.com/mautrix/go/pull/100
|
||||
|
||||
## v0.12.2 (2022-10-16)
|
||||
|
||||
* Added utility method to redact bridge commands.
|
||||
* Added thread ID field to read receipts to match Matrix v1.4 changes.
|
||||
* Added automatic fetching of media repo config at bridge startup to make it
|
||||
easier for bridges to check homeserver media size limits.
|
||||
* Added wrapper for the `/register/available` endpoint.
|
||||
* Added custom user agent to all requests mautrix-go makes. The value can be
|
||||
customized by changing the `DefaultUserAgent` variable.
|
||||
* Implemented [MSC3664], [MSC3862] and [MSC3873] in the push rule evaluator.
|
||||
* Added workaround for potential race conditions in OTK uploads when using
|
||||
appservice encryption ([MSC3202]).
|
||||
* Fixed generating registrations to use `.+` instead of `[0-9]+` in the
|
||||
username regex.
|
||||
* Fixed panic in megolm session listing methods if the store contains withheld
|
||||
key entries.
|
||||
* Fixed missing header in bridge command help messages.
|
||||
|
||||
[MSC3664]: https://github.com/matrix-org/matrix-spec-proposals/pull/3664
|
||||
[MSC3862]: https://github.com/matrix-org/matrix-spec-proposals/pull/3862
|
||||
[MSC3873]: https://github.com/matrix-org/matrix-spec-proposals/pull/3873
|
||||
|
||||
## v0.12.1 (2022-09-16)
|
||||
|
||||
* Bumped minimum Go version to 1.18.
|
||||
* Added `omitempty` for a bunch of fields in response structs to make them more
|
||||
usable for server implementations.
|
||||
* Added `util.RandomToken` to generate GitHub-style access tokens with checksums.
|
||||
* Added utilities to call the push gateway API.
|
||||
* Added `unread_notifications` and [MSC2654] `unread_count` fields to /sync
|
||||
response structs.
|
||||
* Implemented [MSC3870] for uploading and downloading media directly to/from an
|
||||
external media storage like S3.
|
||||
* Fixed dbutil database ownership checks on SQLite.
|
||||
* Fixed typo in unauthorized encryption key withheld code
|
||||
(`m.unauthorized` -> `m.unauthorised`).
|
||||
* Fixed [MSC2409] support to have a separate field for to-device events.
|
||||
|
||||
[MSC2654]: https://github.com/matrix-org/matrix-spec-proposals/pull/2654
|
||||
[MSC3870]: https://github.com/matrix-org/matrix-spec-proposals/pull/3870
|
||||
|
||||
## v0.12.0 (2022-08-16)
|
||||
|
||||
* **Breaking change:** Switched `Client.UserTyping` to take a `time.Duration`
|
||||
instead of raw `int64` milliseconds.
|
||||
* **Breaking change:** Removed custom reply relation type and switched to using
|
||||
the wire format (nesting in `m.in_reply_to`).
|
||||
* Added device ID to appservice OTK count map to match updated [MSC3202].
|
||||
This is also a breaking change, but the previous incorrect behavior wasn't
|
||||
implemented by anything other than mautrix-syncproxy/imessage.
|
||||
* (There are probably other breaking changes too).
|
||||
* Added database utility and schema upgrade framework
|
||||
* Originally from mautrix-whatsapp, but usable for non-bridges too
|
||||
* Includes connection wrapper to log query durations and mutate queries for
|
||||
SQLite compatibility (replacing `$x` with `?x`).
|
||||
* Added bridge utilities similar to mautrix-python. Currently includes:
|
||||
* Crypto helper
|
||||
* Startup flow
|
||||
* Command handling and some standard commands
|
||||
* Double puppeting things
|
||||
* Generic parts of config, basic config validation
|
||||
* Appservice SQL state store
|
||||
* Added alternative markdown spoiler parsing extension that doesn't support
|
||||
reasons, but works better otherwise.
|
||||
* Added Discord underline markdown parsing extension (`_foo_` -> <u>foo</u>).
|
||||
* Added support for parsing spoilers and color tags in the HTML parser.
|
||||
* Added support for mutating plain text nodes in the HTML parser.
|
||||
* Added room version field to the create room request struct.
|
||||
* Added empty JSON object as default request body for all non-GET requests.
|
||||
* Added wrapper for `/capabilities` endpoint.
|
||||
* Added `omitempty` markers for lots of structs to make the structs easier to
|
||||
use on the server side too.
|
||||
* Added support for registering to-device event handlers via the default
|
||||
Syncer's `OnEvent` and `OnEventType` methods.
|
||||
* Fixed `CreateEventContent` using the wrong field name for the room version
|
||||
field.
|
||||
* Fixed `StopSync` not immediately cancelling the sync loop if it was sleeping
|
||||
after a failed sync.
|
||||
* Fixed `GetAvatarURL` always returning the current user's avatar instead of
|
||||
the specified user's avatar (thanks to [@nightmared] in [#83]).
|
||||
* Improved request logging and added new log when a request finishes.
|
||||
* Crypto store improvements:
|
||||
* Deleted devices are now kept in the database.
|
||||
* Made ValidateMessageIndex atomic.
|
||||
* Moved `appservice.RandomString` to the `util` package and made it use
|
||||
`crypto/rand` instead of `math/rand`.
|
||||
* Significantly improved cross-signing validation code.
|
||||
* There are now more options for required trust levels,
|
||||
e.g. you can set `SendKeysMinTrust` to `id.TrustStateCrossSignedTOFU`
|
||||
to trust the first cross-signing master key seen and require all devices
|
||||
to be signed by that key.
|
||||
* Trust state of incoming messages is automatically resolved and stored in
|
||||
`evt.Mautrix.TrustState`. This can be used to reject incoming messages from
|
||||
untrusted devices.
|
||||
|
||||
[@nightmared]: https://github.com/nightmared
|
||||
[#83]: https://github.com/mautrix/go/pull/83
|
||||
|
||||
## v0.11.1 (2023-01-15)
|
||||
|
||||
* Fixed parsing non-positive ordered list start positions in HTML parser
|
||||
(backport of the same fix in v0.13.0).
|
||||
|
||||
## v0.11.0 (2022-05-16)
|
||||
|
||||
* Bumped minimum Go version to 1.17.
|
||||
* Switched from `/r0` to `/v3` paths everywhere.
|
||||
* The new `v3` paths are implemented since Synapse 1.48, Dendrite 0.6.5, and
|
||||
Conduit 0.4.0. Servers older than these are no longer supported.
|
||||
* Switched from blackfriday to goldmark for markdown parsing in the `format`
|
||||
module and added spoiler syntax.
|
||||
* Added `EncryptInPlace` and `DecryptInPlace` methods for attachment encryption.
|
||||
In most cases the plain/ciphertext is not necessary after en/decryption, so
|
||||
the old `Encrypt` and `Decrypt` are deprecated.
|
||||
* Added wrapper for `/rooms/.../aliases`.
|
||||
* Added utility for adding/removing emoji variation selectors to match
|
||||
recommendations on reactions in Matrix.
|
||||
* Added support for async media uploads ([MSC2246]).
|
||||
* Added automatic sleep when receiving 429 error
|
||||
(thanks to [@ownaginatious] in [#44]).
|
||||
* Added support for parsing spec version numbers from the `/versions` endpoint.
|
||||
* Removed unstable prefixed constant used for appservice login.
|
||||
* Fixed URL encoding not working correctly in some cases.
|
||||
|
||||
[MSC2246]: https://github.com/matrix-org/matrix-spec-proposals/pull/2246
|
||||
[@ownaginatious]: https://github.com/ownaginatious
|
||||
[#44]: https://github.com/mautrix/go/pull/44
|
||||
|
||||
## v0.10.12 (2022-03-16)
|
||||
|
||||
* Added option to use a different `Client` to send invites in
|
||||
`IntentAPI.EnsureJoined`.
|
||||
* Changed `MessageEventContent` struct to omit empty `msgtype`s in the output
|
||||
JSON, as sticker events shouldn't have that field.
|
||||
* Fixed deserializing the `thumbnail_file` field in `FileInfo`.
|
||||
* Fixed bug that broke `SQLCryptoStore.FindDeviceByKey`.
|
||||
|
||||
## v0.10.11 (2022-02-16)
|
||||
|
||||
* Added automatic updating of state store from `IntentAPI` calls.
|
||||
* Added option to ignore cache in `IntentAPI.EnsureJoined`.
|
||||
* Added `GetURLPreview` as a wrapper for the `/preview_url` media repo endpoint.
|
||||
* Moved base58 module inline to avoid pulling in btcd as a dependency.
|
||||
|
||||
## v0.10.10 (2022-01-16)
|
||||
|
||||
* Added event types and content structs for server ACLs and moderation policy
|
||||
lists (thanks to [@qua3k] in [#59] and [#60]).
|
||||
* Added optional parameter to `Client.LeaveRoom` to pass a `reason` field.
|
||||
|
||||
[#59]: https://github.com/mautrix/go/pull/59
|
||||
[#60]: https://github.com/mautrix/go/pull/60
|
||||
|
||||
## v0.10.9 (2022-01-04)
|
||||
|
||||
* **Breaking change:** Changed `Messages()` to take a filter as a parameter
|
||||
instead of using the syncer's filter (thanks to [@qua3k] in [#55] and [#56]).
|
||||
* The previous filter behavior was completely broken, as it sent a whole
|
||||
filter instead of just a RoomEventFilter.
|
||||
* Passing `nil` as the filter is fine and will disable filtering
|
||||
(which is equivalent to what it did before with the invalid filter).
|
||||
* Added `Context()` wrapper for the `/context` API (thanks to [@qua3k] in [#54]).
|
||||
* Added utility for converting media files with ffmpeg.
|
||||
|
||||
[#54]: https://github.com/mautrix/go/pull/54
|
||||
[#55]: https://github.com/mautrix/go/pull/55
|
||||
[#56]: https://github.com/mautrix/go/pull/56
|
||||
[@qua3k]: https://github.com/qua3k
|
||||
|
||||
## v0.10.8 (2021-12-30)
|
||||
|
||||
* Added `OlmSession.Describe()` to wrap `olm_session_describe`.
|
||||
* Added trace logs to log olm session descriptions when encrypting/decrypting
|
||||
to-device messages.
|
||||
* Added space event types and content structs.
|
||||
* Added support for power level content override field in `CreateRoom`.
|
||||
* Fixed ordering of olm sessions which would cause an old session to be used in
|
||||
some cases even after a client created a new session.
|
||||
|
||||
## v0.10.7 (2021-12-16)
|
||||
|
||||
* Changed `Client.RedactEvent` to allow arbitrary fields in redaction request.
|
||||
|
||||
## v0.10.5 (2021-12-06)
|
||||
|
||||
* Fixed websocket disconnection not clearing all pending requests.
|
||||
* Added `OlmMachine.SendRoomKeyRequest` as a more direct way of sending room
|
||||
key requests.
|
||||
* Added automatic Olm session recreation if an incoming message fails to decrypt.
|
||||
* Changed `Login` to only omit request content from logs if there's a password
|
||||
or token (appservice logins don't have sensitive content).
|
||||
|
||||
## v0.10.4 (2021-11-25)
|
||||
|
||||
* Added `reason` field to invite and unban requests
|
||||
(thanks to [@ptman] in [#48]).
|
||||
* Fixed `AppService.HasWebsocket()` returning `true` even after websocket has
|
||||
disconnected.
|
||||
|
||||
[@ptman]: https://github.com/ptman
|
||||
[#48]: https://github.com/mautrix/go/pull/48
|
||||
|
||||
## v0.10.3 (2021-11-18)
|
||||
|
||||
* Added logs about incoming appservice transactions.
|
||||
* Added support for message send checkpoints (as HTTP requests, similar to the
|
||||
bridge state reporting system).
|
||||
|
||||
## v0.10.2 (2021-11-15)
|
||||
|
||||
* Added utility method for finding the first supported login flow matching any
|
||||
of the given types.
|
||||
* Updated registering appservice ghosts to use `inhibit_login` flag to prevent
|
||||
lots of unnecessary access tokens from being created.
|
||||
* If you want to log in as an appservice ghost, you should use [MSC2778]'s
|
||||
appservice login (e.g. like [mautrix-whatsapp does for e2be](https://github.com/mautrix/whatsapp/blob/v0.2.1/crypto.go#L143-L149)).
|
||||
|
||||
## v0.10.1 (2021-11-05)
|
||||
|
||||
* Removed direct dependency on `pq`
|
||||
* In order to use some more efficient queries on postgres, you must set
|
||||
`crypto.PostgresArrayWrapper = pq.Array` if you want to use both postgres
|
||||
and e2ee.
|
||||
* Added temporary hack to ignore state events with the MSC2716 historical flag
|
||||
(to be removed after [matrix-org/synapse#11265] is merged)
|
||||
* Added received transaction acknowledgements for websocket appservice
|
||||
transactions.
|
||||
* Added automatic fallback to move `prev_content` from top level to the
|
||||
standard location inside `unsigned`.
|
||||
|
||||
[matrix-org/synapse#11265]: https://github.com/matrix-org/synapse/pull/11265
|
||||
|
||||
## v0.9.31 (2021-10-27)
|
||||
|
||||
* Added `SetEdit` utility function for `MessageEventContent`.
|
||||
|
||||
## v0.9.30 (2021-10-26)
|
||||
|
||||
* Added wrapper for [MSC2716]'s `/batch_send` endpoint.
|
||||
* Added `MarshalJSON` method for `Event` struct to prevent empty unsigned
|
||||
structs from being included in the JSON.
|
||||
|
||||
[MSC2716]: https://github.com/matrix-org/matrix-spec-proposals/pull/2716
|
||||
|
||||
## v0.9.29 (2021-09-30)
|
||||
|
||||
* Added `client.State` method to get full room state.
|
||||
* Added bridge info structs and event types ([MSC2346]).
|
||||
* Made response handling more customizable.
|
||||
* Fixed type of `AuthType` constants.
|
||||
|
||||
[MSC2346]: https://github.com/matrix-org/matrix-spec-proposals/pull/2346
|
||||
|
||||
## v0.9.28 (2021-09-30)
|
||||
|
||||
* Added `X-Mautrix-Process-ID` to appservice websocket headers to help debug
|
||||
issues where multiple instances are connecting to the server at the same time.
|
||||
|
||||
## v0.9.27 (2021-09-23)
|
||||
|
||||
* Fixed Go 1.14 compatibility (broken in v0.9.25).
|
||||
* Added GitHub actions CI to build, test and check formatting on Go 1.14-1.17.
|
||||
|
||||
## v0.9.26 (2021-09-21)
|
||||
|
||||
* Added default no-op logger to `Client` in order to prevent panic when the
|
||||
application doesn't set a logger.
|
||||
|
||||
## v0.9.25 (2021-09-19)
|
||||
|
||||
* Disabled logging request JSON for sensitive requests like `/login`,
|
||||
`/register` and other UIA endpoints. Logging can still be enabled by
|
||||
setting `MAUTRIX_LOG_SENSITIVE_CONTENT` to `yes`.
|
||||
* Added option to store new homeserver URL from `/login` response well-known data.
|
||||
* Added option to stream big sync responses via disk to maybe reduce memory usage.
|
||||
* Fixed trailing slashes in homeserver URL breaking all requests.
|
||||
|
||||
## v0.9.24 (2021-09-03)
|
||||
|
||||
* Added write deadline for appservice websocket connection.
|
||||
|
||||
## v0.9.23 (2021-08-31)
|
||||
|
||||
* Fixed storing e2ee key withheld events in the SQL store.
|
||||
|
||||
## v0.9.22 (2021-08-30)
|
||||
|
||||
* Updated appservice handler to cache multiple recent transaction IDs
|
||||
instead of only the most recent one.
|
||||
|
||||
## v0.9.21 (2021-08-25)
|
||||
|
||||
* Added liveness and readiness endpoints to appservices.
|
||||
* The endpoints are the same as mautrix-python:
|
||||
`/_matrix/mau/live` and `/_matrix/mau/ready`
|
||||
* Liveness always returns 200 and an empty JSON object by default,
|
||||
but it can be turned off by setting `appservice.Live` to `false`.
|
||||
* Readiness defaults to returning 500, and it can be switched to 200
|
||||
by setting `appservice.Ready` to `true`.
|
||||
|
||||
## v0.9.20 (2021-08-19)
|
||||
|
||||
* Added crypto store migration for converting all `VARCHAR(255)` columns
|
||||
to `TEXT` in Postgres databases.
|
||||
|
||||
## v0.9.19 (2021-08-17)
|
||||
|
||||
* Fixed HTML parser outputting two newlines after paragraph tags.
|
||||
|
||||
## v0.9.18 (2021-08-16)
|
||||
|
||||
* Added new `BuildURL` method that does the same as `Client.BuildBaseURL`
|
||||
but without requiring the `Client` instance.
|
||||
|
||||
## v0.9.17 (2021-07-25)
|
||||
|
||||
* Fixed handling OTK counts and device lists coming in through the appservice
|
||||
transaction websocket.
|
||||
* Updated OlmMachine to ignore OTK counts intended for other devices.
|
||||
|
||||
## v0.9.15 (2021-07-16)
|
||||
|
||||
* Added support for [MSC3202] and the to-device part of [MSC2409] in the
|
||||
appservice package.
|
||||
* Added support for sending commands through appservice websocket.
|
||||
* Changed error message JSON field name in appservice error responses to
|
||||
conform with standard Matrix errors (`message` -> `error`).
|
||||
|
||||
[MSC3202]: https://github.com/matrix-org/matrix-spec-proposals/pull/3202
|
||||
|
||||
## v0.9.14 (2021-06-17)
|
||||
|
||||
* Added default implementation of `PillConverter` in HTML parser utility.
|
||||
|
||||
## v0.9.13 (2021-06-15)
|
||||
|
||||
* Added support for parsing and generating encoded matrix.to URLs and `matrix:` URIs ([MSC2312](https://github.com/matrix-org/matrix-doc/pull/2312)).
|
||||
* Updated HTML parser to use new URI parser for parsing user/room pills.
|
||||
|
||||
## v0.9.12 (2021-05-18)
|
||||
|
||||
* Added new method for sending custom data with read receipts
|
||||
(not currently a part of the spec).
|
||||
|
||||
## v0.9.11 (2021-05-12)
|
||||
|
||||
* Improved debug log for unsupported event types.
|
||||
* Added VoIP events to GuessClass.
|
||||
* Added support for parsing strings in VoIP event version field.
|
||||
|
||||
## v0.9.10 (2021-04-29)
|
||||
|
||||
* Fixed `format.RenderMarkdown()` still allowing HTML when both `allowHTML`
|
||||
and `allowMarkdown` are `false`.
|
||||
|
||||
## v0.9.9 (2021-04-26)
|
||||
|
||||
* Updated appservice `StartWebsocket` to return websocket close info.
|
||||
|
||||
## v0.9.8 (2021-04-20)
|
||||
|
||||
* Added methods for getting room tags and account data.
|
||||
|
||||
## v0.9.7 (2021-04-19)
|
||||
|
||||
* **Breaking change (crypto):** `SendEncryptedToDevice` now requires an event
|
||||
type parameter. Previously it only allowed sending events of type
|
||||
`event.ToDeviceForwardedRoomKey`.
|
||||
* Added content structs for VoIP events.
|
||||
* Added global mutex for Olm decryption
|
||||
(previously it was only used for encryption).
|
||||
|
||||
## v0.9.6 (2021-04-15)
|
||||
|
||||
* Added option to retry all HTTP requests when encountering a HTTP network
|
||||
error or gateway error response (502/503/504)
|
||||
* Disabled by default, you need to set the `DefaultHTTPRetries` field in
|
||||
the `AppService` or `Client` struct to enable.
|
||||
* Can also be enabled with `FullRequest`s `MaxAttempts` field.
|
||||
|
||||
## v0.9.5 (2021-04-06)
|
||||
|
||||
* Reverted update of `golang.org/x/sys` which broke Go 1.14 / darwin/arm.
|
||||
|
||||
## v0.9.4 (2021-04-06)
|
||||
|
||||
* Switched appservices to using shared `http.Client` instance with a in-memory
|
||||
cookie jar.
|
||||
|
||||
## v0.9.3 (2021-03-26)
|
||||
|
||||
* Made user agent headers easier to configure.
|
||||
* Improved logging when receiving weird/unhandled to-device events.
|
||||
|
||||
## v0.9.2 (2021-03-15)
|
||||
|
||||
* Fixed type of presence state constants (thanks to [@babolivier] in [#30]).
|
||||
* Implemented presence state fetching methods (thanks to [@babolivier] in [#29]).
|
||||
* Added support for sending and receiving commands via appservice transaction websocket.
|
||||
|
||||
[@babolivier]: https://github.com/babolivier
|
||||
[#29]: https://github.com/mautrix/go/pull/29
|
||||
[#30]: https://github.com/mautrix/go/pull/30
|
||||
|
||||
## v0.9.1 (2021-03-11)
|
||||
|
||||
* Fixed appservice register request hiding actual errors due to UIA error handling.
|
||||
|
||||
## v0.9.0 (2021-03-04)
|
||||
|
||||
* **Breaking change (manual API requests):** `MakeFullRequest` now takes a
|
||||
`FullRequest` struct instead of individual parameters. `MakeRequest`'s
|
||||
parameters are unchanged.
|
||||
* **Breaking change (manual /sync):** `SyncRequest` now requires a `Context`
|
||||
parameter.
|
||||
* **Breaking change (end-to-bridge encryption):**
|
||||
the `uk.half-shot.msc2778.login.application_service` constant used for
|
||||
appservice login ([MSC2778]) was renamed from `AuthTypeAppservice`
|
||||
to `AuthTypeHalfyAppservice`.
|
||||
* The `AuthTypeAppservice` constant now contains `m.login.application_service`,
|
||||
which is currently only used for registrations, but will also be used for
|
||||
login once MSC2778 lands in the spec.
|
||||
* Fixed appservice registration requests to include `m.login.application_service`
|
||||
as the `type` (re [matrix-org/synapse#9548]).
|
||||
* Added wrapper for `/logout/all`.
|
||||
|
||||
[MSC2778]: https://github.com/matrix-org/matrix-spec-proposals/pull/2778
|
||||
[matrix-org/synapse#9548]: https://github.com/matrix-org/synapse/pull/9548
|
||||
|
||||
## v0.8.6 (2021-03-02)
|
||||
|
||||
* Added client-side timeout to `mautrix.Client`'s `http.Client`
|
||||
(defaults to 3 minutes).
|
||||
* Updated maulogger to fix bug where plaintext file logs wouldn't have newlines.
|
||||
|
||||
## v0.8.5 (2021-02-26)
|
||||
|
||||
* Fixed potential concurrent map writes in appservice `Client` and `Intent`
|
||||
methods.
|
||||
|
||||
## v0.8.4 (2021-02-24)
|
||||
|
||||
* Added option to output appservice logs as JSON.
|
||||
* Added new methods for validating user ID localparts.
|
||||
|
||||
## v0.8.3 (2021-02-21)
|
||||
|
||||
* Allowed empty content URIs in parser
|
||||
* Added functions for device management endpoints
|
||||
(thanks to [@edwargix] in [#26]).
|
||||
|
||||
[@edwargix]: https://github.com/edwargix
|
||||
[#26]: https://github.com/mautrix/go/pull/26
|
||||
|
||||
## v0.8.2 (2021-02-09)
|
||||
|
||||
* Fixed error when removing the user's avatar.
|
||||
|
||||
## v0.8.1 (2021-02-09)
|
||||
|
||||
* Added AccountDataStore to remove the need for persistent local storage other
|
||||
than the access token (thanks to [@daenney] in [#23]).
|
||||
* Added support for receiving appservice transactions over websocket.
|
||||
See <https://github.com/mautrix/wsproxy> for the server-side implementation.
|
||||
* Fixed error when removing the room avatar.
|
||||
|
||||
[@daenney]: https://github.com/daenney
|
||||
[#23]: https://github.com/mautrix/go/pull/23
|
||||
|
||||
## v0.8.0 (2020-12-24)
|
||||
|
||||
* **Breaking change:** the `RateLimited` field in the `Registration` struct is
|
||||
now a pointer, so that it can be omitted entirely.
|
||||
* Merged initial SSSS/cross-signing code by [@nikofil]. Interactive verification
|
||||
doesn't work, but the other things mostly do.
|
||||
* Added support for authorization header auth in appservices ([MSC2832]).
|
||||
* Added support for receiving ephemeral events directly ([MSC2409]).
|
||||
* Fixed `SendReaction()` and other similar methods in the `Client` struct.
|
||||
* Fixed crypto cgo code panicking in Go 1.15.3+.
|
||||
* Fixed olm session locks sometime getting deadlocked.
|
||||
|
||||
[MSC2832]: https://github.com/matrix-org/matrix-spec-proposals/pull/2832
|
||||
[MSC2409]: https://github.com/matrix-org/matrix-spec-proposals/pull/2409
|
||||
[@nikofil]: https://github.com/nikofil
|
||||
373
vendor/maunium.net/go/mautrix/LICENSE
generated
vendored
Normal file
373
vendor/maunium.net/go/mautrix/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
24
vendor/maunium.net/go/mautrix/README.md
generated
vendored
Normal file
24
vendor/maunium.net/go/mautrix/README.md
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# mautrix-go
|
||||
[](https://pkg.go.dev/maunium.net/go/mautrix)
|
||||
|
||||
A Golang Matrix framework. Used by [gomuks](https://matrix.org/docs/projects/client/gomuks),
|
||||
[go-neb](https://github.com/matrix-org/go-neb), [mautrix-whatsapp](https://github.com/mautrix/whatsapp)
|
||||
and others.
|
||||
|
||||
Matrix room: [`#maunium:maunium.net`](https://matrix.to/#/#maunium:maunium.net)
|
||||
|
||||
This project is based on [matrix-org/gomatrix](https://github.com/matrix-org/gomatrix).
|
||||
The original project is licensed under [Apache 2.0](https://github.com/matrix-org/gomatrix/blob/master/LICENSE).
|
||||
|
||||
In addition to the basic client API features the original project has, this framework also has:
|
||||
|
||||
* Appservice support (Intent API like mautrix-python, room state storage, etc)
|
||||
* End-to-end encryption support (incl. interactive SAS verification)
|
||||
* Structs for parsing event content
|
||||
* Helpers for parsing and generating Matrix HTML
|
||||
* Helpers for handling push rules
|
||||
|
||||
This project contains modules that are licensed under Apache 2.0:
|
||||
|
||||
* [maunium.net/go/mautrix/crypto/canonicaljson](crypto/canonicaljson)
|
||||
* [maunium.net/go/mautrix/crypto/olm](crypto/olm)
|
||||
350
vendor/maunium.net/go/mautrix/appservice/appservice.go
generated
vendored
Normal file
350
vendor/maunium.net/go/mautrix/appservice/appservice.go
generated
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/rs/zerolog"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
"gopkg.in/yaml.v3"
|
||||
"maunium.net/go/maulogger/v2/maulogadapt"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// EventChannelSize is the size for the Events channel in Appservice instances.
|
||||
var EventChannelSize = 64
|
||||
var OTKChannelSize = 4
|
||||
|
||||
// Create a blank appservice instance.
|
||||
func Create() *AppService {
|
||||
jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||
as := &AppService{
|
||||
Log: zerolog.Nop(),
|
||||
clients: make(map[id.UserID]*mautrix.Client),
|
||||
intents: make(map[id.UserID]*IntentAPI),
|
||||
HTTPClient: &http.Client{Timeout: 180 * time.Second, Jar: jar},
|
||||
StateStore: mautrix.NewMemoryStateStore().(StateStore),
|
||||
Router: mux.NewRouter(),
|
||||
UserAgent: mautrix.DefaultUserAgent,
|
||||
txnIDC: NewTransactionIDCache(128),
|
||||
Live: true,
|
||||
Ready: false,
|
||||
ProcessID: getDefaultProcessID(),
|
||||
|
||||
Events: make(chan *event.Event, EventChannelSize),
|
||||
ToDeviceEvents: make(chan *event.Event, EventChannelSize),
|
||||
OTKCounts: make(chan *mautrix.OTKCount, OTKChannelSize),
|
||||
DeviceLists: make(chan *mautrix.DeviceLists, EventChannelSize),
|
||||
QueryHandler: &QueryHandlerStub{},
|
||||
}
|
||||
|
||||
as.Router.HandleFunc("/transactions/{txnID}", as.PutTransaction).Methods(http.MethodPut)
|
||||
as.Router.HandleFunc("/rooms/{roomAlias}", as.GetRoom).Methods(http.MethodGet)
|
||||
as.Router.HandleFunc("/users/{userID}", as.GetUser).Methods(http.MethodGet)
|
||||
as.Router.HandleFunc("/_matrix/app/v1/transactions/{txnID}", as.PutTransaction).Methods(http.MethodPut)
|
||||
as.Router.HandleFunc("/_matrix/app/v1/rooms/{roomAlias}", as.GetRoom).Methods(http.MethodGet)
|
||||
as.Router.HandleFunc("/_matrix/app/v1/users/{userID}", as.GetUser).Methods(http.MethodGet)
|
||||
as.Router.HandleFunc("/_matrix/app/v1/ping", as.PostPing).Methods(http.MethodPost)
|
||||
as.Router.HandleFunc("/_matrix/app/unstable/fi.mau.msc2659/ping", as.PostPing).Methods(http.MethodPost)
|
||||
as.Router.HandleFunc("/_matrix/mau/live", as.GetLive).Methods(http.MethodGet)
|
||||
as.Router.HandleFunc("/_matrix/mau/ready", as.GetReady).Methods(http.MethodGet)
|
||||
|
||||
return as
|
||||
}
|
||||
|
||||
// QueryHandler handles room alias and user ID queries from the homeserver.
|
||||
type QueryHandler interface {
|
||||
QueryAlias(alias string) bool
|
||||
QueryUser(userID id.UserID) bool
|
||||
}
|
||||
|
||||
type QueryHandlerStub struct{}
|
||||
|
||||
func (qh *QueryHandlerStub) QueryAlias(alias string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (qh *QueryHandlerStub) QueryUser(userID id.UserID) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type WebsocketHandler func(WebsocketCommand) (ok bool, data interface{})
|
||||
|
||||
type StateStore interface {
|
||||
mautrix.StateStore
|
||||
|
||||
IsRegistered(userID id.UserID) bool
|
||||
MarkRegistered(userID id.UserID)
|
||||
|
||||
GetPowerLevel(roomID id.RoomID, userID id.UserID) int
|
||||
GetPowerLevelRequirement(roomID id.RoomID, eventType event.Type) int
|
||||
HasPowerLevel(roomID id.RoomID, userID id.UserID, eventType event.Type) bool
|
||||
}
|
||||
|
||||
// AppService is the main config for all appservices.
|
||||
// It also serves as the appservice instance struct.
|
||||
type AppService struct {
|
||||
HomeserverDomain string
|
||||
hsURLForClient *url.URL
|
||||
Host HostConfig
|
||||
|
||||
Registration *Registration
|
||||
Log zerolog.Logger
|
||||
|
||||
txnIDC *TransactionIDCache
|
||||
|
||||
Events chan *event.Event
|
||||
ToDeviceEvents chan *event.Event
|
||||
DeviceLists chan *mautrix.DeviceLists
|
||||
OTKCounts chan *mautrix.OTKCount
|
||||
QueryHandler QueryHandler
|
||||
StateStore StateStore
|
||||
|
||||
Router *mux.Router
|
||||
UserAgent string
|
||||
server *http.Server
|
||||
HTTPClient *http.Client
|
||||
botClient *mautrix.Client
|
||||
botIntent *IntentAPI
|
||||
|
||||
DefaultHTTPRetries int
|
||||
|
||||
Live bool
|
||||
Ready bool
|
||||
|
||||
clients map[id.UserID]*mautrix.Client
|
||||
clientsLock sync.RWMutex
|
||||
intents map[id.UserID]*IntentAPI
|
||||
intentsLock sync.RWMutex
|
||||
|
||||
ws *websocket.Conn
|
||||
wsWriteLock sync.Mutex
|
||||
StopWebsocket func(error)
|
||||
websocketHandlers map[string]WebsocketHandler
|
||||
websocketHandlersLock sync.RWMutex
|
||||
websocketRequests map[int]chan<- *WebsocketCommand
|
||||
websocketRequestsLock sync.RWMutex
|
||||
websocketRequestID int32
|
||||
// ProcessID is an identifier sent to the websocket proxy for debugging connections
|
||||
ProcessID string
|
||||
|
||||
DoublePuppetValue string
|
||||
GetProfile func(userID id.UserID, roomID id.RoomID) *event.MemberEventContent
|
||||
}
|
||||
|
||||
const DoublePuppetKey = "fi.mau.double_puppet_source"
|
||||
|
||||
func getDefaultProcessID() string {
|
||||
pid := syscall.Getpid()
|
||||
uid := syscall.Getuid()
|
||||
hostname, _ := os.Hostname()
|
||||
return fmt.Sprintf("%s-%d-%d", hostname, uid, pid)
|
||||
}
|
||||
|
||||
func (as *AppService) PrepareWebsocket() {
|
||||
as.websocketHandlersLock.Lock()
|
||||
defer as.websocketHandlersLock.Unlock()
|
||||
if as.websocketHandlers == nil {
|
||||
as.websocketHandlers = make(map[string]WebsocketHandler, 32)
|
||||
as.websocketRequests = make(map[int]chan<- *WebsocketCommand)
|
||||
}
|
||||
}
|
||||
|
||||
// HostConfig contains info about how to host the appservice.
|
||||
type HostConfig struct {
|
||||
Hostname string `yaml:"hostname"`
|
||||
Port uint16 `yaml:"port"`
|
||||
TLSKey string `yaml:"tls_key,omitempty"`
|
||||
TLSCert string `yaml:"tls_cert,omitempty"`
|
||||
}
|
||||
|
||||
// Address gets the whole address of the Appservice.
|
||||
func (hc *HostConfig) Address() string {
|
||||
return fmt.Sprintf("%s:%d", hc.Hostname, hc.Port)
|
||||
}
|
||||
|
||||
func (hc *HostConfig) IsUnixSocket() bool {
|
||||
return strings.HasPrefix(hc.Hostname, "/")
|
||||
}
|
||||
|
||||
func (hc *HostConfig) IsConfigured() bool {
|
||||
return hc.IsUnixSocket() || hc.Port != 0
|
||||
}
|
||||
|
||||
// Save saves this config into a file at the given path.
|
||||
func (as *AppService) Save(path string) error {
|
||||
data, err := yaml.Marshal(as)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, data, 0644)
|
||||
}
|
||||
|
||||
// YAML returns the config in YAML format.
|
||||
func (as *AppService) YAML() (string, error) {
|
||||
data, err := yaml.Marshal(as)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func (as *AppService) BotMXID() id.UserID {
|
||||
return id.NewUserID(as.Registration.SenderLocalpart, as.HomeserverDomain)
|
||||
}
|
||||
|
||||
func (as *AppService) makeIntent(userID id.UserID) *IntentAPI {
|
||||
as.intentsLock.Lock()
|
||||
defer as.intentsLock.Unlock()
|
||||
|
||||
intent, ok := as.intents[userID]
|
||||
if ok {
|
||||
return intent
|
||||
}
|
||||
|
||||
localpart, homeserver, err := userID.Parse()
|
||||
if err != nil || len(localpart) == 0 || homeserver != as.HomeserverDomain {
|
||||
if err != nil {
|
||||
as.Log.Error().Err(err).
|
||||
Str("user_id", userID.String()).
|
||||
Msg("Failed to parse user ID")
|
||||
} else if len(localpart) == 0 {
|
||||
as.Log.Error().Err(err).
|
||||
Str("user_id", userID.String()).
|
||||
Msg("Failed to make intent: localpart is empty")
|
||||
} else if homeserver != as.HomeserverDomain {
|
||||
as.Log.Error().Err(err).
|
||||
Str("user_id", userID.String()).
|
||||
Str("expected_homeserver", as.HomeserverDomain).
|
||||
Msg("Failed to make intent: homeserver doesn't match")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
intent = as.NewIntentAPI(localpart)
|
||||
as.intents[userID] = intent
|
||||
return intent
|
||||
}
|
||||
|
||||
func (as *AppService) Intent(userID id.UserID) *IntentAPI {
|
||||
as.intentsLock.RLock()
|
||||
intent, ok := as.intents[userID]
|
||||
as.intentsLock.RUnlock()
|
||||
if !ok {
|
||||
return as.makeIntent(userID)
|
||||
}
|
||||
return intent
|
||||
}
|
||||
|
||||
func (as *AppService) BotIntent() *IntentAPI {
|
||||
if as.botIntent == nil {
|
||||
as.botIntent = as.makeIntent(as.BotMXID())
|
||||
}
|
||||
return as.botIntent
|
||||
}
|
||||
|
||||
func (as *AppService) SetHomeserverURL(homeserverURL string) error {
|
||||
parsedURL, err := url.Parse(homeserverURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
as.hsURLForClient = parsedURL
|
||||
if as.hsURLForClient.Scheme == "unix" {
|
||||
as.hsURLForClient.Scheme = "http"
|
||||
as.hsURLForClient.Host = "unix"
|
||||
as.hsURLForClient.Path = ""
|
||||
} else if as.hsURLForClient.Scheme == "" {
|
||||
as.hsURLForClient.Scheme = "https"
|
||||
}
|
||||
as.hsURLForClient.RawPath = parsedURL.EscapedPath()
|
||||
|
||||
jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||
as.HTTPClient = &http.Client{Timeout: 180 * time.Second, Jar: jar}
|
||||
if parsedURL.Scheme == "unix" {
|
||||
as.HTTPClient.Transport = &http.Transport{
|
||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
return net.Dial("unix", parsedURL.Path)
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *AppService) NewMautrixClient(userID id.UserID) *mautrix.Client {
|
||||
client := &mautrix.Client{
|
||||
HomeserverURL: as.hsURLForClient,
|
||||
UserID: userID,
|
||||
SetAppServiceUserID: true,
|
||||
AccessToken: as.Registration.AppToken,
|
||||
UserAgent: as.UserAgent,
|
||||
StateStore: as.StateStore,
|
||||
Log: as.Log.With().Str("as_user_id", userID.String()).Logger(),
|
||||
Client: as.HTTPClient,
|
||||
DefaultHTTPRetries: as.DefaultHTTPRetries,
|
||||
}
|
||||
client.Logger = maulogadapt.ZeroAsMau(&client.Log)
|
||||
return client
|
||||
}
|
||||
|
||||
func (as *AppService) NewExternalMautrixClient(userID id.UserID, token string, homeserverURL string) (*mautrix.Client, error) {
|
||||
client := as.NewMautrixClient(userID)
|
||||
client.AccessToken = token
|
||||
if homeserverURL != "" {
|
||||
client.Client = &http.Client{Timeout: 180 * time.Second}
|
||||
var err error
|
||||
client.HomeserverURL, err = mautrix.ParseAndNormalizeBaseURL(homeserverURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (as *AppService) makeClient(userID id.UserID) *mautrix.Client {
|
||||
as.clientsLock.Lock()
|
||||
defer as.clientsLock.Unlock()
|
||||
|
||||
client, ok := as.clients[userID]
|
||||
if !ok {
|
||||
client = as.NewMautrixClient(userID)
|
||||
as.clients[userID] = client
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
func (as *AppService) Client(userID id.UserID) *mautrix.Client {
|
||||
as.clientsLock.RLock()
|
||||
client, ok := as.clients[userID]
|
||||
as.clientsLock.RUnlock()
|
||||
if !ok {
|
||||
return as.makeClient(userID)
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
func (as *AppService) BotClient() *mautrix.Client {
|
||||
if as.botClient == nil {
|
||||
as.botClient = as.makeClient(as.BotMXID())
|
||||
}
|
||||
return as.botClient
|
||||
}
|
||||
175
vendor/maunium.net/go/mautrix/appservice/eventprocessor.go
generated
vendored
Normal file
175
vendor/maunium.net/go/mautrix/appservice/eventprocessor.go
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appservice
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/event"
|
||||
)
|
||||
|
||||
type ExecMode uint8
|
||||
|
||||
const (
|
||||
AsyncHandlers ExecMode = iota
|
||||
AsyncLoop
|
||||
Sync
|
||||
)
|
||||
|
||||
type EventHandler = func(evt *event.Event)
|
||||
type OTKHandler = func(otk *mautrix.OTKCount)
|
||||
type DeviceListHandler = func(lists *mautrix.DeviceLists, since string)
|
||||
|
||||
type EventProcessor struct {
|
||||
ExecMode ExecMode
|
||||
|
||||
as *AppService
|
||||
stop chan struct{}
|
||||
handlers map[event.Type][]EventHandler
|
||||
|
||||
otkHandlers []OTKHandler
|
||||
deviceListHandlers []DeviceListHandler
|
||||
}
|
||||
|
||||
func NewEventProcessor(as *AppService) *EventProcessor {
|
||||
return &EventProcessor{
|
||||
ExecMode: AsyncHandlers,
|
||||
as: as,
|
||||
stop: make(chan struct{}, 1),
|
||||
handlers: make(map[event.Type][]EventHandler),
|
||||
|
||||
otkHandlers: make([]OTKHandler, 0),
|
||||
deviceListHandlers: make([]DeviceListHandler, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) On(evtType event.Type, handler EventHandler) {
|
||||
handlers, ok := ep.handlers[evtType]
|
||||
if !ok {
|
||||
handlers = []EventHandler{handler}
|
||||
} else {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
||||
ep.handlers[evtType] = handlers
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) PrependHandler(evtType event.Type, handler EventHandler) {
|
||||
handlers, ok := ep.handlers[evtType]
|
||||
if !ok {
|
||||
handlers = []EventHandler{handler}
|
||||
} else {
|
||||
handlers = append([]EventHandler{handler}, handlers...)
|
||||
}
|
||||
ep.handlers[evtType] = handlers
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) OnOTK(handler OTKHandler) {
|
||||
ep.otkHandlers = append(ep.otkHandlers, handler)
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) OnDeviceList(handler DeviceListHandler) {
|
||||
ep.deviceListHandlers = append(ep.deviceListHandlers, handler)
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) recoverFunc(data interface{}) {
|
||||
if err := recover(); err != nil {
|
||||
d, _ := json.Marshal(data)
|
||||
ep.as.Log.Error().
|
||||
Str(zerolog.ErrorStackFieldName, string(debug.Stack())).
|
||||
Interface(zerolog.ErrorFieldName, err).
|
||||
Str("event_content", string(d)).
|
||||
Msg("Panic in Matrix event handler")
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) callHandler(handler EventHandler, evt *event.Event) {
|
||||
defer ep.recoverFunc(evt)
|
||||
handler(evt)
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) callOTKHandler(handler OTKHandler, otk *mautrix.OTKCount) {
|
||||
defer ep.recoverFunc(otk)
|
||||
handler(otk)
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) callDeviceListHandler(handler DeviceListHandler, dl *mautrix.DeviceLists) {
|
||||
defer ep.recoverFunc(dl)
|
||||
handler(dl, "")
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) DispatchOTK(otk *mautrix.OTKCount) {
|
||||
for _, handler := range ep.otkHandlers {
|
||||
go ep.callOTKHandler(handler, otk)
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) DispatchDeviceList(dl *mautrix.DeviceLists) {
|
||||
for _, handler := range ep.deviceListHandlers {
|
||||
go ep.callDeviceListHandler(handler, dl)
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) Dispatch(evt *event.Event) {
|
||||
handlers, ok := ep.handlers[evt.Type]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
switch ep.ExecMode {
|
||||
case AsyncHandlers:
|
||||
for _, handler := range handlers {
|
||||
go ep.callHandler(handler, evt)
|
||||
}
|
||||
case AsyncLoop:
|
||||
go func() {
|
||||
for _, handler := range handlers {
|
||||
ep.callHandler(handler, evt)
|
||||
}
|
||||
}()
|
||||
case Sync:
|
||||
for _, handler := range handlers {
|
||||
ep.callHandler(handler, evt)
|
||||
}
|
||||
}
|
||||
}
|
||||
func (ep *EventProcessor) startEvents() {
|
||||
for {
|
||||
select {
|
||||
case evt := <-ep.as.Events:
|
||||
ep.Dispatch(evt)
|
||||
case <-ep.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) startEncryption() {
|
||||
for {
|
||||
select {
|
||||
case evt := <-ep.as.ToDeviceEvents:
|
||||
ep.Dispatch(evt)
|
||||
case otk := <-ep.as.OTKCounts:
|
||||
ep.DispatchOTK(otk)
|
||||
case dl := <-ep.as.DeviceLists:
|
||||
ep.DispatchDeviceList(dl)
|
||||
case <-ep.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) Start() {
|
||||
go ep.startEvents()
|
||||
go ep.startEncryption()
|
||||
}
|
||||
|
||||
func (ep *EventProcessor) Stop() {
|
||||
close(ep.stop)
|
||||
}
|
||||
348
vendor/maunium.net/go/mautrix/appservice/http.go
generated
vendored
Normal file
348
vendor/maunium.net/go/mautrix/appservice/http.go
generated
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// Start starts the HTTP server that listens for calls from the Matrix homeserver.
|
||||
func (as *AppService) Start() {
|
||||
as.server = &http.Server{
|
||||
Handler: as.Router,
|
||||
}
|
||||
var err error
|
||||
if as.Host.IsUnixSocket() {
|
||||
err = as.listenUnix()
|
||||
} else {
|
||||
as.server.Addr = as.Host.Address()
|
||||
err = as.listenTCP()
|
||||
}
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
as.Log.Error().Err(err).Msg("Error in HTTP listener")
|
||||
} else {
|
||||
as.Log.Debug().Msg("HTTP listener stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func (as *AppService) listenUnix() error {
|
||||
socket := as.Host.Hostname
|
||||
_ = syscall.Unlink(socket)
|
||||
defer func() {
|
||||
_ = syscall.Unlink(socket)
|
||||
}()
|
||||
listener, err := net.Listen("unix", socket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
as.Log.Info().Str("socket", socket).Msg("Starting unix socket HTTP listener")
|
||||
return as.server.Serve(listener)
|
||||
}
|
||||
|
||||
func (as *AppService) listenTCP() error {
|
||||
if len(as.Host.TLSCert) == 0 || len(as.Host.TLSKey) == 0 {
|
||||
as.Log.Info().Str("address", as.server.Addr).Msg("Starting HTTP listener")
|
||||
return as.server.ListenAndServe()
|
||||
} else {
|
||||
as.Log.Info().Str("address", as.server.Addr).Msg("Starting HTTP listener with TLS")
|
||||
return as.server.ListenAndServeTLS(as.Host.TLSCert, as.Host.TLSKey)
|
||||
}
|
||||
}
|
||||
|
||||
func (as *AppService) Stop() {
|
||||
if as.server == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
_ = as.server.Shutdown(ctx)
|
||||
as.server = nil
|
||||
}
|
||||
|
||||
// CheckServerToken checks if the given request originated from the Matrix homeserver.
|
||||
func (as *AppService) CheckServerToken(w http.ResponseWriter, r *http.Request) (isValid bool) {
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if len(authHeader) > 0 && strings.HasPrefix(authHeader, "Bearer ") {
|
||||
isValid = authHeader[len("Bearer "):] == as.Registration.ServerToken
|
||||
} else {
|
||||
queryToken := r.URL.Query().Get("access_token")
|
||||
if len(queryToken) > 0 {
|
||||
isValid = queryToken == as.Registration.ServerToken
|
||||
} else {
|
||||
Error{
|
||||
ErrorCode: ErrUnknownToken,
|
||||
HTTPStatus: http.StatusForbidden,
|
||||
Message: "Missing access token",
|
||||
}.Write(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
if !isValid {
|
||||
Error{
|
||||
ErrorCode: ErrUnknownToken,
|
||||
HTTPStatus: http.StatusForbidden,
|
||||
Message: "Incorrect access token",
|
||||
}.Write(w)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PutTransaction handles a /transactions PUT call from the homeserver.
|
||||
func (as *AppService) PutTransaction(w http.ResponseWriter, r *http.Request) {
|
||||
if !as.CheckServerToken(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
txnID := vars["txnID"]
|
||||
if len(txnID) == 0 {
|
||||
Error{
|
||||
ErrorCode: ErrNoTransactionID,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Message: "Missing transaction ID",
|
||||
}.Write(w)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil || len(body) == 0 {
|
||||
Error{
|
||||
ErrorCode: ErrNotJSON,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Message: "Missing request body",
|
||||
}.Write(w)
|
||||
return
|
||||
}
|
||||
log := as.Log.With().Str("transaction_id", txnID).Logger()
|
||||
ctx := context.Background()
|
||||
ctx = log.WithContext(ctx)
|
||||
if as.txnIDC.IsProcessed(txnID) {
|
||||
// Duplicate transaction ID: no-op
|
||||
WriteBlankOK(w)
|
||||
log.Debug().Msg("Ignoring duplicate transaction")
|
||||
return
|
||||
}
|
||||
|
||||
var txn Transaction
|
||||
err = json.Unmarshal(body, &txn)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to parse transaction content")
|
||||
Error{
|
||||
ErrorCode: ErrBadJSON,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Message: "Failed to parse body JSON",
|
||||
}.Write(w)
|
||||
} else {
|
||||
as.handleTransaction(ctx, txnID, &txn)
|
||||
WriteBlankOK(w)
|
||||
}
|
||||
}
|
||||
|
||||
func (as *AppService) handleTransaction(ctx context.Context, id string, txn *Transaction) {
|
||||
log := zerolog.Ctx(ctx)
|
||||
log.Debug().Object("content", txn).Msg("Starting handling of transaction")
|
||||
if as.Registration.EphemeralEvents {
|
||||
if txn.EphemeralEvents != nil {
|
||||
as.handleEvents(ctx, txn.EphemeralEvents, event.EphemeralEventType)
|
||||
} else if txn.MSC2409EphemeralEvents != nil {
|
||||
as.handleEvents(ctx, txn.MSC2409EphemeralEvents, event.EphemeralEventType)
|
||||
}
|
||||
if txn.ToDeviceEvents != nil {
|
||||
as.handleEvents(ctx, txn.ToDeviceEvents, event.ToDeviceEventType)
|
||||
} else if txn.MSC2409ToDeviceEvents != nil {
|
||||
as.handleEvents(ctx, txn.MSC2409ToDeviceEvents, event.ToDeviceEventType)
|
||||
}
|
||||
}
|
||||
as.handleEvents(ctx, txn.Events, event.UnknownEventType)
|
||||
if txn.DeviceLists != nil {
|
||||
as.handleDeviceLists(ctx, txn.DeviceLists)
|
||||
} else if txn.MSC3202DeviceLists != nil {
|
||||
as.handleDeviceLists(ctx, txn.MSC3202DeviceLists)
|
||||
}
|
||||
if txn.DeviceOTKCount != nil {
|
||||
as.handleOTKCounts(ctx, txn.DeviceOTKCount)
|
||||
} else if txn.MSC3202DeviceOTKCount != nil {
|
||||
as.handleOTKCounts(ctx, txn.MSC3202DeviceOTKCount)
|
||||
}
|
||||
as.txnIDC.MarkProcessed(id)
|
||||
log.Debug().Msg("Finished dispatching events from transaction")
|
||||
}
|
||||
|
||||
func (as *AppService) handleOTKCounts(ctx context.Context, otks OTKCountMap) {
|
||||
for userID, devices := range otks {
|
||||
for deviceID, otkCounts := range devices {
|
||||
otkCounts.UserID = userID
|
||||
otkCounts.DeviceID = deviceID
|
||||
select {
|
||||
case as.OTKCounts <- &otkCounts:
|
||||
default:
|
||||
zerolog.Ctx(ctx).Warn().
|
||||
Str("user_id", userID.String()).
|
||||
Msg("Dropped OTK count update for user because channel is full")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (as *AppService) handleDeviceLists(ctx context.Context, dl *mautrix.DeviceLists) {
|
||||
select {
|
||||
case as.DeviceLists <- dl:
|
||||
default:
|
||||
zerolog.Ctx(ctx).Warn().Msg("Dropped device list update because channel is full")
|
||||
}
|
||||
}
|
||||
|
||||
func (as *AppService) handleEvents(ctx context.Context, evts []*event.Event, defaultTypeClass event.TypeClass) {
|
||||
log := zerolog.Ctx(ctx)
|
||||
for _, evt := range evts {
|
||||
evt.Mautrix.ReceivedAt = time.Now()
|
||||
if defaultTypeClass != event.UnknownEventType {
|
||||
evt.Type.Class = defaultTypeClass
|
||||
} else if evt.StateKey != nil {
|
||||
evt.Type.Class = event.StateEventType
|
||||
} else {
|
||||
evt.Type.Class = event.MessageEventType
|
||||
}
|
||||
err := evt.Content.ParseRaw(evt.Type)
|
||||
if errors.Is(err, event.ErrUnsupportedContentType) {
|
||||
log.Debug().Str("event_id", evt.ID.String()).Msg("Not parsing content of unsupported event")
|
||||
} else if err != nil {
|
||||
log.Warn().Err(err).
|
||||
Str("event_id", evt.ID.String()).
|
||||
Str("event_type", evt.Type.Type).
|
||||
Str("event_type_class", evt.Type.Class.Name()).
|
||||
Msg("Failed to parse content of event")
|
||||
}
|
||||
|
||||
if evt.Type.IsState() {
|
||||
// TODO remove this check after making sure the log doesn't happen
|
||||
historical, ok := evt.Content.Raw["org.matrix.msc2716.historical"].(bool)
|
||||
if ok && historical {
|
||||
log.Warn().
|
||||
Str("event_id", evt.ID.String()).
|
||||
Str("event_type", evt.Type.Type).
|
||||
Str("state_key", evt.GetStateKey()).
|
||||
Msg("Received historical state event")
|
||||
} else {
|
||||
mautrix.UpdateStateStore(as.StateStore, evt)
|
||||
}
|
||||
}
|
||||
var ch chan *event.Event
|
||||
if evt.Type.Class == event.ToDeviceEventType {
|
||||
ch = as.ToDeviceEvents
|
||||
} else {
|
||||
ch = as.Events
|
||||
}
|
||||
select {
|
||||
case ch <- evt:
|
||||
default:
|
||||
log.Warn().
|
||||
Str("event_id", evt.ID.String()).
|
||||
Str("event_type", evt.Type.Type).
|
||||
Str("event_type_class", evt.Type.Class.Name()).
|
||||
Msg("Event channel is full")
|
||||
ch <- evt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetRoom handles a /rooms GET call from the homeserver.
|
||||
func (as *AppService) GetRoom(w http.ResponseWriter, r *http.Request) {
|
||||
if !as.CheckServerToken(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
roomAlias := vars["roomAlias"]
|
||||
ok := as.QueryHandler.QueryAlias(roomAlias)
|
||||
if ok {
|
||||
WriteBlankOK(w)
|
||||
} else {
|
||||
Error{
|
||||
ErrorCode: ErrUnknown,
|
||||
HTTPStatus: http.StatusNotFound,
|
||||
}.Write(w)
|
||||
}
|
||||
}
|
||||
|
||||
// GetUser handles a /users GET call from the homeserver.
|
||||
func (as *AppService) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
if !as.CheckServerToken(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
userID := id.UserID(vars["userID"])
|
||||
ok := as.QueryHandler.QueryUser(userID)
|
||||
if ok {
|
||||
WriteBlankOK(w)
|
||||
} else {
|
||||
Error{
|
||||
ErrorCode: ErrUnknown,
|
||||
HTTPStatus: http.StatusNotFound,
|
||||
}.Write(w)
|
||||
}
|
||||
}
|
||||
|
||||
func (as *AppService) PostPing(w http.ResponseWriter, r *http.Request) {
|
||||
if !as.CheckServerToken(w, r) {
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil || len(body) == 0 || !json.Valid(body) {
|
||||
Error{
|
||||
ErrorCode: ErrNotJSON,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Message: "Missing request body",
|
||||
}.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
var txn mautrix.ReqAppservicePing
|
||||
_ = json.Unmarshal(body, &txn)
|
||||
as.Log.Debug().Str("txn_id", txn.TxnID).Msg("Received ping from homeserver")
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("{}"))
|
||||
}
|
||||
|
||||
func (as *AppService) GetLive(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
if as.Live {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
w.Write([]byte("{}"))
|
||||
}
|
||||
|
||||
func (as *AppService) GetReady(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
if as.Ready {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
w.Write([]byte("{}"))
|
||||
}
|
||||
419
vendor/maunium.net/go/mautrix/appservice/intent.go
generated
vendored
Normal file
419
vendor/maunium.net/go/mautrix/appservice/intent.go
generated
vendored
Normal file
@@ -0,0 +1,419 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appservice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type IntentAPI struct {
|
||||
*mautrix.Client
|
||||
bot *mautrix.Client
|
||||
as *AppService
|
||||
Localpart string
|
||||
UserID id.UserID
|
||||
|
||||
IsCustomPuppet bool
|
||||
}
|
||||
|
||||
func (as *AppService) NewIntentAPI(localpart string) *IntentAPI {
|
||||
userID := id.NewUserID(localpart, as.HomeserverDomain)
|
||||
bot := as.BotClient()
|
||||
if userID == bot.UserID {
|
||||
bot = nil
|
||||
}
|
||||
return &IntentAPI{
|
||||
Client: as.Client(userID),
|
||||
bot: bot,
|
||||
as: as,
|
||||
Localpart: localpart,
|
||||
UserID: userID,
|
||||
|
||||
IsCustomPuppet: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) Register() error {
|
||||
_, _, err := intent.Client.Register(&mautrix.ReqRegister{
|
||||
Username: intent.Localpart,
|
||||
Type: mautrix.AuthTypeAppservice,
|
||||
InhibitLogin: true,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) EnsureRegistered() error {
|
||||
if intent.IsCustomPuppet || intent.as.StateStore.IsRegistered(intent.UserID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := intent.Register()
|
||||
if err != nil && !errors.Is(err, mautrix.MUserInUse) {
|
||||
return fmt.Errorf("failed to ensure registered: %w", err)
|
||||
}
|
||||
intent.as.StateStore.MarkRegistered(intent.UserID)
|
||||
return nil
|
||||
}
|
||||
|
||||
type EnsureJoinedParams struct {
|
||||
IgnoreCache bool
|
||||
BotOverride *mautrix.Client
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) EnsureJoined(roomID id.RoomID, extra ...EnsureJoinedParams) error {
|
||||
var params EnsureJoinedParams
|
||||
if len(extra) > 1 {
|
||||
panic("invalid number of extra parameters")
|
||||
} else if len(extra) == 1 {
|
||||
params = extra[0]
|
||||
}
|
||||
if intent.as.StateStore.IsInRoom(roomID, intent.UserID) && !params.IgnoreCache {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := intent.EnsureRegistered(); err != nil {
|
||||
return fmt.Errorf("failed to ensure joined: %w", err)
|
||||
}
|
||||
|
||||
resp, err := intent.JoinRoomByID(roomID)
|
||||
if err != nil {
|
||||
bot := intent.bot
|
||||
if params.BotOverride != nil {
|
||||
bot = params.BotOverride
|
||||
}
|
||||
if !errors.Is(err, mautrix.MForbidden) || bot == nil {
|
||||
return fmt.Errorf("failed to ensure joined: %w", err)
|
||||
}
|
||||
_, inviteErr := bot.InviteUser(roomID, &mautrix.ReqInviteUser{
|
||||
UserID: intent.UserID,
|
||||
})
|
||||
if inviteErr != nil {
|
||||
return fmt.Errorf("failed to invite in ensure joined: %w", inviteErr)
|
||||
}
|
||||
resp, err = intent.JoinRoomByID(roomID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to ensure joined after invite: %w", err)
|
||||
}
|
||||
}
|
||||
intent.as.StateStore.SetMembership(resp.RoomID, intent.UserID, event.MembershipJoin)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) AddDoublePuppetValue(into interface{}) interface{} {
|
||||
if !intent.IsCustomPuppet || intent.as.DoublePuppetValue == "" {
|
||||
return into
|
||||
}
|
||||
switch val := into.(type) {
|
||||
case *map[string]interface{}:
|
||||
if *val == nil {
|
||||
valNonPtr := make(map[string]interface{})
|
||||
*val = valNonPtr
|
||||
}
|
||||
(*val)[DoublePuppetKey] = intent.as.DoublePuppetValue
|
||||
return val
|
||||
case map[string]interface{}:
|
||||
val[DoublePuppetKey] = intent.as.DoublePuppetValue
|
||||
return val
|
||||
case *event.Content:
|
||||
if val.Raw == nil {
|
||||
val.Raw = make(map[string]interface{})
|
||||
}
|
||||
val.Raw[DoublePuppetKey] = intent.as.DoublePuppetValue
|
||||
return val
|
||||
case event.Content:
|
||||
if val.Raw == nil {
|
||||
val.Raw = make(map[string]interface{})
|
||||
}
|
||||
val.Raw[DoublePuppetKey] = intent.as.DoublePuppetValue
|
||||
return val
|
||||
default:
|
||||
return &event.Content{
|
||||
Raw: map[string]interface{}{
|
||||
DoublePuppetKey: intent.as.DoublePuppetValue,
|
||||
},
|
||||
Parsed: val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SendMessageEvent(roomID id.RoomID, eventType event.Type, contentJSON interface{}) (*mautrix.RespSendEvent, error) {
|
||||
if err := intent.EnsureJoined(roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentJSON = intent.AddDoublePuppetValue(contentJSON)
|
||||
return intent.Client.SendMessageEvent(roomID, eventType, contentJSON)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SendMassagedMessageEvent(roomID id.RoomID, eventType event.Type, contentJSON interface{}, ts int64) (*mautrix.RespSendEvent, error) {
|
||||
if err := intent.EnsureJoined(roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentJSON = intent.AddDoublePuppetValue(contentJSON)
|
||||
return intent.Client.SendMessageEvent(roomID, eventType, contentJSON, mautrix.ReqSendEvent{Timestamp: ts})
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SendStateEvent(roomID id.RoomID, eventType event.Type, stateKey string, contentJSON interface{}) (*mautrix.RespSendEvent, error) {
|
||||
if eventType != event.StateMember || stateKey != string(intent.UserID) {
|
||||
if err := intent.EnsureJoined(roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
contentJSON = intent.AddDoublePuppetValue(contentJSON)
|
||||
return intent.Client.SendStateEvent(roomID, eventType, stateKey, contentJSON)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SendMassagedStateEvent(roomID id.RoomID, eventType event.Type, stateKey string, contentJSON interface{}, ts int64) (*mautrix.RespSendEvent, error) {
|
||||
if err := intent.EnsureJoined(roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentJSON = intent.AddDoublePuppetValue(contentJSON)
|
||||
return intent.Client.SendMassagedStateEvent(roomID, eventType, stateKey, contentJSON, ts)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) StateEvent(roomID id.RoomID, eventType event.Type, stateKey string, outContent interface{}) error {
|
||||
if err := intent.EnsureJoined(roomID); err != nil {
|
||||
return err
|
||||
}
|
||||
return intent.Client.StateEvent(roomID, eventType, stateKey, outContent)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) State(roomID id.RoomID) (mautrix.RoomStateMap, error) {
|
||||
if err := intent.EnsureJoined(roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return intent.Client.State(roomID)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SendCustomMembershipEvent(roomID id.RoomID, target id.UserID, membership event.Membership, reason string, extraContent ...map[string]interface{}) (*mautrix.RespSendEvent, error) {
|
||||
content := &event.MemberEventContent{
|
||||
Membership: membership,
|
||||
Reason: reason,
|
||||
}
|
||||
memberContent, ok := intent.as.StateStore.TryGetMember(roomID, target)
|
||||
if !ok {
|
||||
if intent.as.GetProfile != nil {
|
||||
memberContent = intent.as.GetProfile(target, roomID)
|
||||
ok = memberContent != nil
|
||||
}
|
||||
if !ok {
|
||||
profile, err := intent.GetProfile(target)
|
||||
if err != nil {
|
||||
intent.Log.Debug().Err(err).
|
||||
Str("target_user_id", target.String()).
|
||||
Str("membership", string(membership)).
|
||||
Msg("Failed to get profile to fill new membership event")
|
||||
} else {
|
||||
content.Displayname = profile.DisplayName
|
||||
content.AvatarURL = profile.AvatarURL.CUString()
|
||||
}
|
||||
}
|
||||
}
|
||||
if ok && memberContent != nil {
|
||||
content.Displayname = memberContent.Displayname
|
||||
content.AvatarURL = memberContent.AvatarURL
|
||||
}
|
||||
var extra map[string]interface{}
|
||||
if len(extraContent) > 0 {
|
||||
extra = extraContent[0]
|
||||
}
|
||||
return intent.SendStateEvent(roomID, event.StateMember, target.String(), &event.Content{
|
||||
Parsed: content,
|
||||
Raw: extra,
|
||||
})
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) JoinRoomByID(roomID id.RoomID, extraContent ...map[string]interface{}) (resp *mautrix.RespJoinRoom, err error) {
|
||||
if intent.IsCustomPuppet || len(extraContent) > 0 {
|
||||
_, err = intent.SendCustomMembershipEvent(roomID, intent.UserID, event.MembershipJoin, "", extraContent...)
|
||||
return &mautrix.RespJoinRoom{}, err
|
||||
}
|
||||
return intent.Client.JoinRoomByID(roomID)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) LeaveRoom(roomID id.RoomID, extra ...interface{}) (resp *mautrix.RespLeaveRoom, err error) {
|
||||
var extraContent map[string]interface{}
|
||||
leaveReq := &mautrix.ReqLeave{}
|
||||
for _, item := range extra {
|
||||
switch val := item.(type) {
|
||||
case map[string]interface{}:
|
||||
extraContent = val
|
||||
case *mautrix.ReqLeave:
|
||||
leaveReq = val
|
||||
}
|
||||
}
|
||||
if intent.IsCustomPuppet || extraContent != nil {
|
||||
_, err = intent.SendCustomMembershipEvent(roomID, intent.UserID, event.MembershipLeave, leaveReq.Reason, extraContent)
|
||||
return &mautrix.RespLeaveRoom{}, err
|
||||
}
|
||||
return intent.Client.LeaveRoom(roomID, leaveReq)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) InviteUser(roomID id.RoomID, req *mautrix.ReqInviteUser, extraContent ...map[string]interface{}) (resp *mautrix.RespInviteUser, err error) {
|
||||
if intent.IsCustomPuppet || len(extraContent) > 0 {
|
||||
_, err = intent.SendCustomMembershipEvent(roomID, req.UserID, event.MembershipInvite, req.Reason, extraContent...)
|
||||
return &mautrix.RespInviteUser{}, err
|
||||
}
|
||||
return intent.Client.InviteUser(roomID, req)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) KickUser(roomID id.RoomID, req *mautrix.ReqKickUser, extraContent ...map[string]interface{}) (resp *mautrix.RespKickUser, err error) {
|
||||
if intent.IsCustomPuppet || len(extraContent) > 0 {
|
||||
_, err = intent.SendCustomMembershipEvent(roomID, req.UserID, event.MembershipLeave, req.Reason, extraContent...)
|
||||
return &mautrix.RespKickUser{}, err
|
||||
}
|
||||
return intent.Client.KickUser(roomID, req)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) BanUser(roomID id.RoomID, req *mautrix.ReqBanUser, extraContent ...map[string]interface{}) (resp *mautrix.RespBanUser, err error) {
|
||||
if intent.IsCustomPuppet || len(extraContent) > 0 {
|
||||
_, err = intent.SendCustomMembershipEvent(roomID, req.UserID, event.MembershipBan, req.Reason, extraContent...)
|
||||
return &mautrix.RespBanUser{}, err
|
||||
}
|
||||
return intent.Client.BanUser(roomID, req)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) UnbanUser(roomID id.RoomID, req *mautrix.ReqUnbanUser, extraContent ...map[string]interface{}) (resp *mautrix.RespUnbanUser, err error) {
|
||||
if intent.IsCustomPuppet || len(extraContent) > 0 {
|
||||
_, err = intent.SendCustomMembershipEvent(roomID, req.UserID, event.MembershipLeave, req.Reason, extraContent...)
|
||||
return &mautrix.RespUnbanUser{}, err
|
||||
}
|
||||
return intent.Client.UnbanUser(roomID, req)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) Member(roomID id.RoomID, userID id.UserID) *event.MemberEventContent {
|
||||
member, ok := intent.as.StateStore.TryGetMember(roomID, userID)
|
||||
if !ok {
|
||||
_ = intent.StateEvent(roomID, event.StateMember, string(userID), &member)
|
||||
}
|
||||
return member
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) PowerLevels(roomID id.RoomID) (pl *event.PowerLevelsEventContent, err error) {
|
||||
pl = intent.as.StateStore.GetPowerLevels(roomID)
|
||||
if pl == nil {
|
||||
pl = &event.PowerLevelsEventContent{}
|
||||
err = intent.StateEvent(roomID, event.StatePowerLevels, "", pl)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SetPowerLevels(roomID id.RoomID, levels *event.PowerLevelsEventContent) (resp *mautrix.RespSendEvent, err error) {
|
||||
return intent.SendStateEvent(roomID, event.StatePowerLevels, "", &levels)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SetPowerLevel(roomID id.RoomID, userID id.UserID, level int) (*mautrix.RespSendEvent, error) {
|
||||
pl, err := intent.PowerLevels(roomID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pl.GetUserLevel(userID) != level {
|
||||
pl.SetUserLevel(userID, level)
|
||||
return intent.SendStateEvent(roomID, event.StatePowerLevels, "", &pl)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SendText(roomID id.RoomID, text string) (*mautrix.RespSendEvent, error) {
|
||||
if err := intent.EnsureJoined(roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return intent.Client.SendText(roomID, text)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SendNotice(roomID id.RoomID, text string) (*mautrix.RespSendEvent, error) {
|
||||
if err := intent.EnsureJoined(roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return intent.Client.SendNotice(roomID, text)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) RedactEvent(roomID id.RoomID, eventID id.EventID, extra ...mautrix.ReqRedact) (*mautrix.RespSendEvent, error) {
|
||||
if err := intent.EnsureJoined(roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var req mautrix.ReqRedact
|
||||
if len(extra) > 0 {
|
||||
req = extra[0]
|
||||
}
|
||||
intent.AddDoublePuppetValue(&req.Extra)
|
||||
return intent.Client.RedactEvent(roomID, eventID, req)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SetRoomName(roomID id.RoomID, roomName string) (*mautrix.RespSendEvent, error) {
|
||||
return intent.SendStateEvent(roomID, event.StateRoomName, "", map[string]interface{}{
|
||||
"name": roomName,
|
||||
})
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SetRoomAvatar(roomID id.RoomID, avatarURL id.ContentURI) (*mautrix.RespSendEvent, error) {
|
||||
return intent.SendStateEvent(roomID, event.StateRoomAvatar, "", map[string]interface{}{
|
||||
"url": avatarURL.String(),
|
||||
})
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SetRoomTopic(roomID id.RoomID, topic string) (*mautrix.RespSendEvent, error) {
|
||||
return intent.SendStateEvent(roomID, event.StateTopic, "", map[string]interface{}{
|
||||
"topic": topic,
|
||||
})
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SetDisplayName(displayName string) error {
|
||||
if err := intent.EnsureRegistered(); err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := intent.Client.GetOwnDisplayName()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check current displayname: %w", err)
|
||||
} else if resp.DisplayName == displayName {
|
||||
// No need to update
|
||||
return nil
|
||||
}
|
||||
return intent.Client.SetDisplayName(displayName)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) SetAvatarURL(avatarURL id.ContentURI) error {
|
||||
if err := intent.EnsureRegistered(); err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := intent.Client.GetOwnAvatarURL()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check current avatar URL: %w", err)
|
||||
} else if resp.FileID == avatarURL.FileID && resp.Homeserver == avatarURL.Homeserver {
|
||||
// No need to update
|
||||
return nil
|
||||
}
|
||||
return intent.Client.SetAvatarURL(avatarURL)
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) Whoami() (*mautrix.RespWhoami, error) {
|
||||
if err := intent.EnsureRegistered(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return intent.Client.Whoami()
|
||||
}
|
||||
|
||||
func (intent *IntentAPI) EnsureInvited(roomID id.RoomID, userID id.UserID) error {
|
||||
if !intent.as.StateStore.IsInvited(roomID, userID) {
|
||||
_, err := intent.InviteUser(roomID, &mautrix.ReqInviteUser{
|
||||
UserID: userID,
|
||||
})
|
||||
if httpErr, ok := err.(mautrix.HTTPError); ok &&
|
||||
httpErr.RespError != nil &&
|
||||
(strings.Contains(httpErr.RespError.Err, "is already in the room") || strings.Contains(httpErr.RespError.Err, "is already joined to room")) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
152
vendor/maunium.net/go/mautrix/appservice/protocol.go
generated
vendored
Normal file
152
vendor/maunium.net/go/mautrix/appservice/protocol.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appservice
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type OTKCountMap = map[id.UserID]map[id.DeviceID]mautrix.OTKCount
|
||||
type FallbackKeyMap = map[id.UserID]map[id.DeviceID][]id.KeyAlgorithm
|
||||
|
||||
// Transaction contains a list of events.
|
||||
type Transaction struct {
|
||||
Events []*event.Event `json:"events"`
|
||||
EphemeralEvents []*event.Event `json:"ephemeral,omitempty"`
|
||||
ToDeviceEvents []*event.Event `json:"to_device,omitempty"`
|
||||
|
||||
DeviceLists *mautrix.DeviceLists `json:"device_lists,omitempty"`
|
||||
DeviceOTKCount OTKCountMap `json:"device_one_time_keys_count,omitempty"`
|
||||
FallbackKeys FallbackKeyMap `json:"device_unused_fallback_key_types,omitempty"`
|
||||
|
||||
MSC2409EphemeralEvents []*event.Event `json:"de.sorunome.msc2409.ephemeral,omitempty"`
|
||||
MSC2409ToDeviceEvents []*event.Event `json:"de.sorunome.msc2409.to_device,omitempty"`
|
||||
MSC3202DeviceLists *mautrix.DeviceLists `json:"org.matrix.msc3202.device_lists,omitempty"`
|
||||
MSC3202DeviceOTKCount OTKCountMap `json:"org.matrix.msc3202.device_one_time_keys_count,omitempty"`
|
||||
MSC3202FallbackKeys FallbackKeyMap `json:"org.matrix.msc3202.device_unused_fallback_key_types,omitempty"`
|
||||
}
|
||||
|
||||
func (txn *Transaction) MarshalZerologObject(ctx *zerolog.Event) {
|
||||
ctx.Int("pdu", len(txn.Events))
|
||||
if txn.EphemeralEvents != nil {
|
||||
ctx.Int("edu", len(txn.EphemeralEvents))
|
||||
} else if txn.MSC2409EphemeralEvents != nil {
|
||||
ctx.Int("unstable_edu", len(txn.MSC2409EphemeralEvents))
|
||||
}
|
||||
if txn.ToDeviceEvents != nil {
|
||||
ctx.Int("to_device", len(txn.ToDeviceEvents))
|
||||
} else if txn.MSC2409ToDeviceEvents != nil {
|
||||
ctx.Int("unstable_to_device", len(txn.MSC2409ToDeviceEvents))
|
||||
}
|
||||
if len(txn.DeviceOTKCount) > 0 {
|
||||
ctx.Int("otk_count_users", len(txn.DeviceOTKCount))
|
||||
} else if len(txn.MSC3202DeviceOTKCount) > 0 {
|
||||
ctx.Int("unstable_otk_count_users", len(txn.MSC3202DeviceOTKCount))
|
||||
}
|
||||
if txn.DeviceLists != nil {
|
||||
ctx.Int("device_changes", len(txn.DeviceLists.Changed))
|
||||
} else if txn.MSC3202DeviceLists != nil {
|
||||
ctx.Int("unstable_device_changes", len(txn.MSC3202DeviceLists.Changed))
|
||||
}
|
||||
if txn.FallbackKeys != nil {
|
||||
ctx.Int("fallback_key_users", len(txn.FallbackKeys))
|
||||
} else if txn.MSC3202FallbackKeys != nil {
|
||||
ctx.Int("unstable_fallback_key_users", len(txn.MSC3202FallbackKeys))
|
||||
}
|
||||
}
|
||||
|
||||
func (txn *Transaction) ContentString() string {
|
||||
var parts []string
|
||||
if len(txn.Events) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%d PDUs", len(txn.Events)))
|
||||
}
|
||||
if len(txn.EphemeralEvents) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%d EDUs", len(txn.EphemeralEvents)))
|
||||
} else if len(txn.MSC2409EphemeralEvents) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%d EDUs (unstable)", len(txn.MSC2409EphemeralEvents)))
|
||||
}
|
||||
if len(txn.ToDeviceEvents) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%d to-device events", len(txn.ToDeviceEvents)))
|
||||
} else if len(txn.MSC2409ToDeviceEvents) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%d to-device events (unstable)", len(txn.MSC2409ToDeviceEvents)))
|
||||
}
|
||||
if len(txn.DeviceOTKCount) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("OTK counts for %d users", len(txn.DeviceOTKCount)))
|
||||
} else if len(txn.MSC3202DeviceOTKCount) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("OTK counts for %d users (unstable)", len(txn.MSC3202DeviceOTKCount)))
|
||||
}
|
||||
if txn.DeviceLists != nil {
|
||||
parts = append(parts, fmt.Sprintf("%d device list changes", len(txn.DeviceLists.Changed)))
|
||||
} else if txn.MSC3202DeviceLists != nil {
|
||||
parts = append(parts, fmt.Sprintf("%d device list changes (unstable)", len(txn.MSC3202DeviceLists.Changed)))
|
||||
}
|
||||
if txn.FallbackKeys != nil {
|
||||
parts = append(parts, fmt.Sprintf("unused fallback key counts for %d users", len(txn.FallbackKeys)))
|
||||
} else if txn.MSC3202FallbackKeys != nil {
|
||||
parts = append(parts, fmt.Sprintf("unused fallback key counts for %d users (unstable)", len(txn.MSC3202FallbackKeys)))
|
||||
}
|
||||
return strings.Join(parts, ", ")
|
||||
}
|
||||
|
||||
// EventListener is a function that receives events.
|
||||
type EventListener func(evt *event.Event)
|
||||
|
||||
// WriteBlankOK writes a blank OK message as a reply to a HTTP request.
|
||||
func WriteBlankOK(w http.ResponseWriter) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("{}"))
|
||||
}
|
||||
|
||||
// Respond responds to a HTTP request with a JSON object.
|
||||
func Respond(w http.ResponseWriter, data interface{}) error {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
dataStr, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(dataStr)
|
||||
return err
|
||||
}
|
||||
|
||||
// Error represents a Matrix protocol error.
|
||||
type Error struct {
|
||||
HTTPStatus int `json:"-"`
|
||||
ErrorCode ErrorCode `json:"errcode"`
|
||||
Message string `json:"error"`
|
||||
}
|
||||
|
||||
func (err Error) Write(w http.ResponseWriter) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(err.HTTPStatus)
|
||||
_ = Respond(w, &err)
|
||||
}
|
||||
|
||||
// ErrorCode is the machine-readable code in an Error.
|
||||
type ErrorCode string
|
||||
|
||||
// Native ErrorCodes
|
||||
const (
|
||||
ErrUnknownToken ErrorCode = "M_UNKNOWN_TOKEN"
|
||||
ErrBadJSON ErrorCode = "M_BAD_JSON"
|
||||
ErrNotJSON ErrorCode = "M_NOT_JSON"
|
||||
ErrUnknown ErrorCode = "M_UNKNOWN"
|
||||
)
|
||||
|
||||
// Custom ErrorCodes
|
||||
const (
|
||||
ErrNoTransactionID ErrorCode = "NET.MAUNIUM.NO_TRANSACTION_ID"
|
||||
)
|
||||
100
vendor/maunium.net/go/mautrix/appservice/registration.go
generated
vendored
Normal file
100
vendor/maunium.net/go/mautrix/appservice/registration.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appservice
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"maunium.net/go/mautrix/util"
|
||||
)
|
||||
|
||||
// Registration contains the data in a Matrix appservice registration.
|
||||
// See https://spec.matrix.org/v1.2/application-service-api/#registration
|
||||
type Registration struct {
|
||||
ID string `yaml:"id" json:"id"`
|
||||
URL string `yaml:"url" json:"url"`
|
||||
AppToken string `yaml:"as_token" json:"as_token"`
|
||||
ServerToken string `yaml:"hs_token" json:"hs_token"`
|
||||
SenderLocalpart string `yaml:"sender_localpart" json:"sender_localpart"`
|
||||
RateLimited *bool `yaml:"rate_limited,omitempty" json:"rate_limited,omitempty"`
|
||||
Namespaces Namespaces `yaml:"namespaces" json:"namespaces"`
|
||||
Protocols []string `yaml:"protocols,omitempty" json:"protocols,omitempty"`
|
||||
|
||||
SoruEphemeralEvents bool `yaml:"de.sorunome.msc2409.push_ephemeral,omitempty" json:"de.sorunome.msc2409.push_ephemeral,omitempty"`
|
||||
EphemeralEvents bool `yaml:"push_ephemeral,omitempty" json:"push_ephemeral,omitempty"`
|
||||
}
|
||||
|
||||
// CreateRegistration creates a Registration with random appservice and homeserver tokens.
|
||||
func CreateRegistration() *Registration {
|
||||
return &Registration{
|
||||
AppToken: util.RandomString(64),
|
||||
ServerToken: util.RandomString(64),
|
||||
}
|
||||
}
|
||||
|
||||
// LoadRegistration loads a YAML file and turns it into a Registration.
|
||||
func LoadRegistration(path string) (*Registration, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reg := &Registration{}
|
||||
err = yaml.Unmarshal(data, reg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reg, nil
|
||||
}
|
||||
|
||||
// Save saves this Registration into a file at the given path.
|
||||
func (reg *Registration) Save(path string) error {
|
||||
data, err := yaml.Marshal(reg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, data, 0600)
|
||||
}
|
||||
|
||||
// YAML returns the registration in YAML format.
|
||||
func (reg *Registration) YAML() (string, error) {
|
||||
data, err := yaml.Marshal(reg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// Namespaces contains the three areas that appservices can reserve parts of.
|
||||
type Namespaces struct {
|
||||
UserIDs NamespaceList `yaml:"users,omitempty" json:"users,omitempty"`
|
||||
RoomAliases NamespaceList `yaml:"aliases,omitempty" json:"aliases,omitempty"`
|
||||
RoomIDs NamespaceList `yaml:"rooms,omitempty" json:"rooms,omitempty"`
|
||||
}
|
||||
|
||||
// Namespace is a reserved namespace in any area.
|
||||
type Namespace struct {
|
||||
Regex string `yaml:"regex" json:"regex"`
|
||||
Exclusive bool `yaml:"exclusive" json:"exclusive"`
|
||||
}
|
||||
|
||||
type NamespaceList []Namespace
|
||||
|
||||
func (nsl *NamespaceList) Register(regex *regexp.Regexp, exclusive bool) {
|
||||
ns := Namespace{
|
||||
Regex: regex.String(),
|
||||
Exclusive: exclusive,
|
||||
}
|
||||
if nsl == nil {
|
||||
*nsl = []Namespace{ns}
|
||||
} else {
|
||||
*nsl = append(*nsl, ns)
|
||||
}
|
||||
}
|
||||
43
vendor/maunium.net/go/mautrix/appservice/txnid.go
generated
vendored
Normal file
43
vendor/maunium.net/go/mautrix/appservice/txnid.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appservice
|
||||
|
||||
import "sync"
|
||||
|
||||
type TransactionIDCache struct {
|
||||
array []string
|
||||
arrayPtr int
|
||||
hash map[string]struct{}
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewTransactionIDCache(size int) *TransactionIDCache {
|
||||
return &TransactionIDCache{
|
||||
array: make([]string, size),
|
||||
hash: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (txnIDC *TransactionIDCache) IsProcessed(txnID string) bool {
|
||||
txnIDC.lock.RLock()
|
||||
_, exists := txnIDC.hash[txnID]
|
||||
txnIDC.lock.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
func (txnIDC *TransactionIDCache) MarkProcessed(txnID string) {
|
||||
txnIDC.lock.Lock()
|
||||
txnIDC.hash[txnID] = struct{}{}
|
||||
if txnIDC.array[txnIDC.arrayPtr] != "" {
|
||||
for i := 0; i < len(txnIDC.array)/8; i++ {
|
||||
delete(txnIDC.hash, txnIDC.array[txnIDC.arrayPtr+i])
|
||||
txnIDC.array[txnIDC.arrayPtr+i] = ""
|
||||
}
|
||||
}
|
||||
txnIDC.array[txnIDC.arrayPtr] = txnID
|
||||
txnIDC.lock.Unlock()
|
||||
}
|
||||
408
vendor/maunium.net/go/mautrix/appservice/websocket.go
generated
vendored
Normal file
408
vendor/maunium.net/go/mautrix/appservice/websocket.go
generated
vendored
Normal file
@@ -0,0 +1,408 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
type WebsocketRequest struct {
|
||||
ReqID int `json:"id,omitempty"`
|
||||
Command string `json:"command"`
|
||||
Data interface{} `json:"data"`
|
||||
|
||||
Deadline time.Duration `json:"-"`
|
||||
}
|
||||
|
||||
type WebsocketCommand struct {
|
||||
ReqID int `json:"id,omitempty"`
|
||||
Command string `json:"command"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
|
||||
Ctx context.Context `json:"-"`
|
||||
}
|
||||
|
||||
func (wsc *WebsocketCommand) MakeResponse(ok bool, data interface{}) *WebsocketRequest {
|
||||
if wsc.ReqID == 0 || wsc.Command == "response" || wsc.Command == "error" {
|
||||
return nil
|
||||
}
|
||||
cmd := "response"
|
||||
if !ok {
|
||||
cmd = "error"
|
||||
}
|
||||
if err, isError := data.(error); isError {
|
||||
var errorData json.RawMessage
|
||||
var jsonErr error
|
||||
unwrappedErr := err
|
||||
var prefixMessage string
|
||||
for unwrappedErr != nil {
|
||||
errorData, jsonErr = json.Marshal(unwrappedErr)
|
||||
if errorData != nil && len(errorData) > 2 && jsonErr == nil {
|
||||
prefixMessage = strings.Replace(err.Error(), unwrappedErr.Error(), "", 1)
|
||||
prefixMessage = strings.TrimRight(prefixMessage, ": ")
|
||||
break
|
||||
}
|
||||
unwrappedErr = errors.Unwrap(unwrappedErr)
|
||||
}
|
||||
if errorData != nil {
|
||||
if !gjson.GetBytes(errorData, "message").Exists() {
|
||||
errorData, _ = sjson.SetBytes(errorData, "message", err.Error())
|
||||
} // else: marshaled error contains a message already
|
||||
} else {
|
||||
errorData, _ = sjson.SetBytes(nil, "message", err.Error())
|
||||
}
|
||||
if len(prefixMessage) > 0 {
|
||||
errorData, _ = sjson.SetBytes(errorData, "prefix_message", prefixMessage)
|
||||
}
|
||||
data = errorData
|
||||
}
|
||||
return &WebsocketRequest{
|
||||
ReqID: wsc.ReqID,
|
||||
Command: cmd,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
type WebsocketTransaction struct {
|
||||
Status string `json:"status"`
|
||||
TxnID string `json:"txn_id"`
|
||||
Transaction
|
||||
}
|
||||
|
||||
type WebsocketTransactionResponse struct {
|
||||
TxnID string `json:"txn_id"`
|
||||
}
|
||||
|
||||
type WebsocketMessage struct {
|
||||
WebsocketTransaction
|
||||
WebsocketCommand
|
||||
}
|
||||
|
||||
const (
|
||||
WebsocketCloseConnReplaced = 4001
|
||||
WebsocketCloseTxnNotAcknowledged = 4002
|
||||
)
|
||||
|
||||
type MeowWebsocketCloseCode string
|
||||
|
||||
const (
|
||||
MeowServerShuttingDown MeowWebsocketCloseCode = "server_shutting_down"
|
||||
MeowConnectionReplaced MeowWebsocketCloseCode = "conn_replaced"
|
||||
MeowTxnNotAcknowledged MeowWebsocketCloseCode = "transactions_not_acknowledged"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrWebsocketManualStop = errors.New("the websocket was disconnected manually")
|
||||
ErrWebsocketOverridden = errors.New("a new call to StartWebsocket overrode the previous connection")
|
||||
ErrWebsocketUnknownError = errors.New("an unknown error occurred")
|
||||
|
||||
ErrWebsocketNotConnected = errors.New("websocket not connected")
|
||||
ErrWebsocketClosed = errors.New("websocket closed before response received")
|
||||
)
|
||||
|
||||
func (mwcc MeowWebsocketCloseCode) String() string {
|
||||
switch mwcc {
|
||||
case MeowServerShuttingDown:
|
||||
return "the server is shutting down"
|
||||
case MeowConnectionReplaced:
|
||||
return "the connection was replaced by another client"
|
||||
case MeowTxnNotAcknowledged:
|
||||
return "transactions were not acknowledged"
|
||||
default:
|
||||
return string(mwcc)
|
||||
}
|
||||
}
|
||||
|
||||
type CloseCommand struct {
|
||||
Code int `json:"-"`
|
||||
Command string `json:"command"`
|
||||
Status MeowWebsocketCloseCode `json:"status"`
|
||||
}
|
||||
|
||||
func (cc CloseCommand) Error() string {
|
||||
return fmt.Sprintf("websocket: close %d: %s", cc.Code, cc.Status.String())
|
||||
}
|
||||
|
||||
func parseCloseError(err error) error {
|
||||
closeError := &websocket.CloseError{}
|
||||
if !errors.As(err, &closeError) {
|
||||
return err
|
||||
}
|
||||
var closeCommand CloseCommand
|
||||
closeCommand.Code = closeError.Code
|
||||
closeCommand.Command = "disconnect"
|
||||
if len(closeError.Text) > 0 {
|
||||
jsonErr := json.Unmarshal([]byte(closeError.Text), &closeCommand)
|
||||
if jsonErr != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(closeCommand.Status) == 0 {
|
||||
if closeCommand.Code == WebsocketCloseConnReplaced {
|
||||
closeCommand.Status = MeowConnectionReplaced
|
||||
} else if closeCommand.Code == websocket.CloseServiceRestart {
|
||||
closeCommand.Status = MeowServerShuttingDown
|
||||
}
|
||||
}
|
||||
return &closeCommand
|
||||
}
|
||||
|
||||
func (as *AppService) HasWebsocket() bool {
|
||||
return as.ws != nil
|
||||
}
|
||||
|
||||
func (as *AppService) SendWebsocket(cmd *WebsocketRequest) error {
|
||||
ws := as.ws
|
||||
if cmd == nil {
|
||||
return nil
|
||||
} else if ws == nil {
|
||||
return ErrWebsocketNotConnected
|
||||
}
|
||||
as.wsWriteLock.Lock()
|
||||
defer as.wsWriteLock.Unlock()
|
||||
if cmd.Deadline == 0 {
|
||||
cmd.Deadline = 3 * time.Minute
|
||||
}
|
||||
_ = ws.SetWriteDeadline(time.Now().Add(cmd.Deadline))
|
||||
return ws.WriteJSON(cmd)
|
||||
}
|
||||
|
||||
func (as *AppService) clearWebsocketResponseWaiters() {
|
||||
as.websocketRequestsLock.Lock()
|
||||
for _, waiter := range as.websocketRequests {
|
||||
waiter <- &WebsocketCommand{Command: "__websocket_closed"}
|
||||
}
|
||||
as.websocketRequests = make(map[int]chan<- *WebsocketCommand)
|
||||
as.websocketRequestsLock.Unlock()
|
||||
}
|
||||
|
||||
func (as *AppService) addWebsocketResponseWaiter(reqID int, waiter chan<- *WebsocketCommand) {
|
||||
as.websocketRequestsLock.Lock()
|
||||
as.websocketRequests[reqID] = waiter
|
||||
as.websocketRequestsLock.Unlock()
|
||||
}
|
||||
|
||||
func (as *AppService) removeWebsocketResponseWaiter(reqID int, waiter chan<- *WebsocketCommand) {
|
||||
as.websocketRequestsLock.Lock()
|
||||
existingWaiter, ok := as.websocketRequests[reqID]
|
||||
if ok && existingWaiter == waiter {
|
||||
delete(as.websocketRequests, reqID)
|
||||
}
|
||||
close(waiter)
|
||||
as.websocketRequestsLock.Unlock()
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (er *ErrorResponse) Error() string {
|
||||
return fmt.Sprintf("%s: %s", er.Code, er.Message)
|
||||
}
|
||||
|
||||
func (as *AppService) RequestWebsocket(ctx context.Context, cmd *WebsocketRequest, response interface{}) error {
|
||||
cmd.ReqID = int(atomic.AddInt32(&as.websocketRequestID, 1))
|
||||
respChan := make(chan *WebsocketCommand, 1)
|
||||
as.addWebsocketResponseWaiter(cmd.ReqID, respChan)
|
||||
defer as.removeWebsocketResponseWaiter(cmd.ReqID, respChan)
|
||||
err := as.SendWebsocket(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case resp := <-respChan:
|
||||
if resp.Command == "__websocket_closed" {
|
||||
return ErrWebsocketClosed
|
||||
} else if resp.Command == "error" {
|
||||
var respErr ErrorResponse
|
||||
err = json.Unmarshal(resp.Data, &respErr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse error JSON: %w", err)
|
||||
}
|
||||
return &respErr
|
||||
} else if response != nil {
|
||||
err = json.Unmarshal(resp.Data, &response)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse response JSON: %w", err)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (as *AppService) unknownCommandHandler(cmd WebsocketCommand) (bool, interface{}) {
|
||||
zerolog.Ctx(cmd.Ctx).Warn().Msg("No handler for websocket command")
|
||||
return false, fmt.Errorf("unknown request type")
|
||||
}
|
||||
|
||||
func (as *AppService) SetWebsocketCommandHandler(cmd string, handler WebsocketHandler) {
|
||||
as.websocketHandlersLock.Lock()
|
||||
as.websocketHandlers[cmd] = handler
|
||||
as.websocketHandlersLock.Unlock()
|
||||
}
|
||||
|
||||
func (as *AppService) consumeWebsocket(stopFunc func(error), ws *websocket.Conn) {
|
||||
defer stopFunc(ErrWebsocketUnknownError)
|
||||
ctx := context.Background()
|
||||
for {
|
||||
var msg WebsocketMessage
|
||||
err := ws.ReadJSON(&msg)
|
||||
if err != nil {
|
||||
as.Log.Debug().Err(err).Msg("Error reading from websocket")
|
||||
stopFunc(parseCloseError(err))
|
||||
return
|
||||
}
|
||||
with := as.Log.With().
|
||||
Int("req_id", msg.ReqID).
|
||||
Str("ws_command", msg.Command)
|
||||
if msg.TxnID != "" {
|
||||
with = with.Str("transaction_id", msg.TxnID)
|
||||
}
|
||||
log := with.Logger()
|
||||
ctx = log.WithContext(ctx)
|
||||
if msg.Command == "" || msg.Command == "transaction" {
|
||||
if msg.TxnID == "" || !as.txnIDC.IsProcessed(msg.TxnID) {
|
||||
as.handleTransaction(ctx, msg.TxnID, &msg.Transaction)
|
||||
} else {
|
||||
log.Debug().
|
||||
Object("content", &msg.Transaction).
|
||||
Msg("Ignoring duplicate transaction")
|
||||
}
|
||||
go func() {
|
||||
err = as.SendWebsocket(msg.MakeResponse(true, &WebsocketTransactionResponse{TxnID: msg.TxnID}))
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to send response to websocket transaction")
|
||||
} else {
|
||||
log.Debug().Msg("Sent response to transaction")
|
||||
}
|
||||
}()
|
||||
} else if msg.Command == "connect" {
|
||||
log.Debug().Msg("Websocket connect confirmation received")
|
||||
} else if msg.Command == "response" || msg.Command == "error" {
|
||||
as.websocketRequestsLock.RLock()
|
||||
respChan, ok := as.websocketRequests[msg.ReqID]
|
||||
if ok {
|
||||
select {
|
||||
case respChan <- &msg.WebsocketCommand:
|
||||
default:
|
||||
log.Warn().Msg("Failed to handle response: channel didn't accept response")
|
||||
}
|
||||
} else {
|
||||
log.Warn().Msg("Dropping response to unknown request ID")
|
||||
}
|
||||
as.websocketRequestsLock.RUnlock()
|
||||
} else {
|
||||
log.Debug().Msg("Received websocket command")
|
||||
as.websocketHandlersLock.RLock()
|
||||
handler, ok := as.websocketHandlers[msg.Command]
|
||||
as.websocketHandlersLock.RUnlock()
|
||||
if !ok {
|
||||
handler = as.unknownCommandHandler
|
||||
}
|
||||
go func() {
|
||||
okResp, data := handler(msg.WebsocketCommand)
|
||||
err = as.SendWebsocket(msg.MakeResponse(okResp, data))
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send response to websocket command")
|
||||
} else if okResp {
|
||||
log.Debug().Msg("Sent success response to websocket command")
|
||||
} else {
|
||||
log.Debug().Msg("Sent error response to websocket command")
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (as *AppService) StartWebsocket(baseURL string, onConnect func()) error {
|
||||
parsed, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse URL: %w", err)
|
||||
}
|
||||
parsed.Path = filepath.Join(parsed.Path, "_matrix/client/unstable/fi.mau.as_sync")
|
||||
if parsed.Scheme == "http" {
|
||||
parsed.Scheme = "ws"
|
||||
} else if parsed.Scheme == "https" {
|
||||
parsed.Scheme = "wss"
|
||||
}
|
||||
ws, resp, err := websocket.DefaultDialer.Dial(parsed.String(), http.Header{
|
||||
"Authorization": []string{fmt.Sprintf("Bearer %s", as.Registration.AppToken)},
|
||||
"User-Agent": []string{as.BotClient().UserAgent},
|
||||
|
||||
"X-Mautrix-Process-ID": []string{as.ProcessID},
|
||||
"X-Mautrix-Websocket-Version": []string{"3"},
|
||||
})
|
||||
if resp != nil && resp.StatusCode >= 400 {
|
||||
var errResp Error
|
||||
err = json.NewDecoder(resp.Body).Decode(&errResp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("websocket request returned HTTP %d with non-JSON body", resp.StatusCode)
|
||||
} else {
|
||||
return fmt.Errorf("websocket request returned %s (HTTP %d): %s", errResp.ErrorCode, resp.StatusCode, errResp.Message)
|
||||
}
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to open websocket: %w", err)
|
||||
}
|
||||
if as.StopWebsocket != nil {
|
||||
as.StopWebsocket(ErrWebsocketOverridden)
|
||||
}
|
||||
closeChan := make(chan error)
|
||||
closeChanOnce := sync.Once{}
|
||||
stopFunc := func(err error) {
|
||||
closeChanOnce.Do(func() {
|
||||
closeChan <- err
|
||||
})
|
||||
}
|
||||
as.ws = ws
|
||||
as.StopWebsocket = stopFunc
|
||||
as.PrepareWebsocket()
|
||||
as.Log.Debug().Msg("Appservice transaction websocket opened")
|
||||
|
||||
go as.consumeWebsocket(stopFunc, ws)
|
||||
|
||||
if onConnect != nil {
|
||||
onConnect()
|
||||
}
|
||||
|
||||
closeErr := <-closeChan
|
||||
|
||||
if as.ws == ws {
|
||||
as.clearWebsocketResponseWaiters()
|
||||
as.ws = nil
|
||||
}
|
||||
|
||||
_ = ws.SetWriteDeadline(time.Now().Add(3 * time.Second))
|
||||
err = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseGoingAway, ""))
|
||||
if err != nil && !errors.Is(err, websocket.ErrCloseSent) {
|
||||
as.Log.Warn().Err(err).Msg("Error writing close message to websocket")
|
||||
}
|
||||
err = ws.Close()
|
||||
if err != nil {
|
||||
as.Log.Warn().Err(err).Msg("Error closing websocket")
|
||||
}
|
||||
return closeErr
|
||||
}
|
||||
2023
vendor/maunium.net/go/mautrix/client.go
generated
vendored
Normal file
2023
vendor/maunium.net/go/mautrix/client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
239
vendor/maunium.net/go/mautrix/crypto/attachment/attachments.go
generated
vendored
Normal file
239
vendor/maunium.net/go/mautrix/crypto/attachment/attachments.go
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package attachment
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"maunium.net/go/mautrix/crypto/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
HashMismatch = errors.New("mismatching SHA-256 digest")
|
||||
UnsupportedVersion = errors.New("unsupported Matrix file encryption version")
|
||||
UnsupportedAlgorithm = errors.New("unsupported JWK encryption algorithm")
|
||||
InvalidKey = errors.New("failed to decode key")
|
||||
InvalidInitVector = errors.New("failed to decode initialization vector")
|
||||
InvalidHash = errors.New("failed to decode SHA-256 hash")
|
||||
ReaderClosed = errors.New("encrypting reader was already closed")
|
||||
)
|
||||
|
||||
var (
|
||||
keyBase64Length = base64.RawURLEncoding.EncodedLen(utils.AESCTRKeyLength)
|
||||
ivBase64Length = base64.RawStdEncoding.EncodedLen(utils.AESCTRIVLength)
|
||||
hashBase64Length = base64.RawStdEncoding.EncodedLen(utils.SHAHashLength)
|
||||
)
|
||||
|
||||
type JSONWebKey struct {
|
||||
Key string `json:"k"`
|
||||
Algorithm string `json:"alg"`
|
||||
Extractable bool `json:"ext"`
|
||||
KeyType string `json:"kty"`
|
||||
KeyOps []string `json:"key_ops"`
|
||||
}
|
||||
|
||||
type EncryptedFileHashes struct {
|
||||
SHA256 string `json:"sha256"`
|
||||
}
|
||||
|
||||
type decodedKeys struct {
|
||||
key [utils.AESCTRKeyLength]byte
|
||||
iv [utils.AESCTRIVLength]byte
|
||||
|
||||
sha256 [utils.SHAHashLength]byte
|
||||
}
|
||||
|
||||
type EncryptedFile struct {
|
||||
Key JSONWebKey `json:"key"`
|
||||
InitVector string `json:"iv"`
|
||||
Hashes EncryptedFileHashes `json:"hashes"`
|
||||
Version string `json:"v"`
|
||||
|
||||
decoded *decodedKeys
|
||||
}
|
||||
|
||||
func NewEncryptedFile() *EncryptedFile {
|
||||
key, iv := utils.GenAttachmentA256CTR()
|
||||
return &EncryptedFile{
|
||||
Key: JSONWebKey{
|
||||
Key: base64.RawURLEncoding.EncodeToString(key[:]),
|
||||
Algorithm: "A256CTR",
|
||||
Extractable: true,
|
||||
KeyType: "oct",
|
||||
KeyOps: []string{"encrypt", "decrypt"},
|
||||
},
|
||||
InitVector: base64.RawStdEncoding.EncodeToString(iv[:]),
|
||||
Version: "v2",
|
||||
|
||||
decoded: &decodedKeys{key: key, iv: iv},
|
||||
}
|
||||
}
|
||||
|
||||
func (ef *EncryptedFile) decodeKeys(includeHash bool) error {
|
||||
if ef.decoded != nil {
|
||||
return nil
|
||||
} else if len(ef.Key.Key) != keyBase64Length {
|
||||
return InvalidKey
|
||||
} else if len(ef.InitVector) != ivBase64Length {
|
||||
return InvalidInitVector
|
||||
} else if includeHash && len(ef.Hashes.SHA256) != hashBase64Length {
|
||||
return InvalidHash
|
||||
}
|
||||
ef.decoded = &decodedKeys{}
|
||||
_, err := base64.RawURLEncoding.Decode(ef.decoded.key[:], []byte(ef.Key.Key))
|
||||
if err != nil {
|
||||
return InvalidKey
|
||||
}
|
||||
_, err = base64.RawStdEncoding.Decode(ef.decoded.iv[:], []byte(ef.InitVector))
|
||||
if err != nil {
|
||||
return InvalidInitVector
|
||||
}
|
||||
if includeHash {
|
||||
_, err = base64.RawStdEncoding.Decode(ef.decoded.sha256[:], []byte(ef.Hashes.SHA256))
|
||||
if err != nil {
|
||||
return InvalidHash
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts the given data, updates the SHA256 hash in the EncryptedFile struct and returns the ciphertext.
|
||||
//
|
||||
// Deprecated: this makes a copy for the ciphertext, which means 2x memory usage. EncryptInPlace is recommended.
|
||||
func (ef *EncryptedFile) Encrypt(plaintext []byte) []byte {
|
||||
ciphertext := make([]byte, len(plaintext))
|
||||
copy(ciphertext, plaintext)
|
||||
ef.EncryptInPlace(ciphertext)
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
// EncryptInPlace encrypts the given data in-place (i.e. the provided data is overridden with the ciphertext)
|
||||
// and updates the SHA256 hash in the EncryptedFile struct.
|
||||
func (ef *EncryptedFile) EncryptInPlace(data []byte) {
|
||||
ef.decodeKeys(false)
|
||||
utils.XorA256CTR(data, ef.decoded.key, ef.decoded.iv)
|
||||
checksum := sha256.Sum256(data)
|
||||
ef.Hashes.SHA256 = base64.RawStdEncoding.EncodeToString(checksum[:])
|
||||
}
|
||||
|
||||
type encryptingReader struct {
|
||||
stream cipher.Stream
|
||||
hash hash.Hash
|
||||
source io.Reader
|
||||
file *EncryptedFile
|
||||
closed bool
|
||||
|
||||
isDecrypting bool
|
||||
}
|
||||
|
||||
func (r *encryptingReader) Read(dst []byte) (n int, err error) {
|
||||
if r.closed {
|
||||
return 0, ReaderClosed
|
||||
} else if r.isDecrypting && r.file.decoded == nil {
|
||||
if err = r.file.PrepareForDecryption(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
n, err = r.source.Read(dst)
|
||||
r.stream.XORKeyStream(dst[:n], dst[:n])
|
||||
r.hash.Write(dst[:n])
|
||||
return
|
||||
}
|
||||
|
||||
func (r *encryptingReader) Close() (err error) {
|
||||
closer, ok := r.source.(io.ReadCloser)
|
||||
if ok {
|
||||
err = closer.Close()
|
||||
}
|
||||
if r.isDecrypting {
|
||||
var downloadedChecksum [utils.SHAHashLength]byte
|
||||
r.hash.Sum(downloadedChecksum[:])
|
||||
if downloadedChecksum != r.file.decoded.sha256 {
|
||||
return HashMismatch
|
||||
}
|
||||
} else {
|
||||
r.file.Hashes.SHA256 = base64.RawStdEncoding.EncodeToString(r.hash.Sum(nil))
|
||||
}
|
||||
r.closed = true
|
||||
return
|
||||
}
|
||||
|
||||
// EncryptStream wraps the given io.Reader in order to encrypt the data.
|
||||
//
|
||||
// The Close() method of the returned io.ReadCloser must be called for the SHA256 hash
|
||||
// in the EncryptedFile struct to be updated. The metadata is not valid before the hash
|
||||
// is filled.
|
||||
func (ef *EncryptedFile) EncryptStream(reader io.Reader) io.ReadCloser {
|
||||
ef.decodeKeys(false)
|
||||
block, _ := aes.NewCipher(ef.decoded.key[:])
|
||||
return &encryptingReader{
|
||||
stream: cipher.NewCTR(block, ef.decoded.iv[:]),
|
||||
hash: sha256.New(),
|
||||
source: reader,
|
||||
file: ef,
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt decrypts the given data and returns the plaintext.
|
||||
//
|
||||
// Deprecated: this makes a copy for the plaintext data, which means 2x memory usage. DecryptInPlace is recommended.
|
||||
func (ef *EncryptedFile) Decrypt(ciphertext []byte) ([]byte, error) {
|
||||
plaintext := make([]byte, len(ciphertext))
|
||||
copy(plaintext, ciphertext)
|
||||
return plaintext, ef.DecryptInPlace(plaintext)
|
||||
}
|
||||
|
||||
// PrepareForDecryption checks that the version and algorithm are supported and decodes the base64 keys
|
||||
//
|
||||
// DecryptStream will call this with the first Read() call if this hasn't been called manually.
|
||||
//
|
||||
// DecryptInPlace will always call this automatically, so calling this manually is not necessary when using that function.
|
||||
func (ef *EncryptedFile) PrepareForDecryption() error {
|
||||
if ef.Version != "v2" {
|
||||
return UnsupportedVersion
|
||||
} else if ef.Key.Algorithm != "A256CTR" {
|
||||
return UnsupportedAlgorithm
|
||||
} else if err := ef.decodeKeys(true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecryptInPlace decrypts the given data in-place (i.e. the provided data is overridden with the plaintext).
|
||||
func (ef *EncryptedFile) DecryptInPlace(data []byte) error {
|
||||
if err := ef.PrepareForDecryption(); err != nil {
|
||||
return err
|
||||
} else if ef.decoded.sha256 != sha256.Sum256(data) {
|
||||
return HashMismatch
|
||||
} else {
|
||||
utils.XorA256CTR(data, ef.decoded.key, ef.decoded.iv)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DecryptStream wraps the given io.Reader in order to decrypt the data.
|
||||
//
|
||||
// The first Read call will check the algorithm and decode keys, so it might return an error before actually reading anything.
|
||||
// If you want to validate the file before opening the stream, call PrepareForDecryption manually and check for errors.
|
||||
//
|
||||
// The Close call will validate the hash and return an error if it doesn't match.
|
||||
// In this case, the written data should be considered compromised and should not be used further.
|
||||
func (ef *EncryptedFile) DecryptStream(reader io.Reader) io.ReadCloser {
|
||||
block, _ := aes.NewCipher(ef.decoded.key[:])
|
||||
return &encryptingReader{
|
||||
stream: cipher.NewCTR(block, ef.decoded.iv[:]),
|
||||
hash: sha256.New(),
|
||||
source: reader,
|
||||
file: ef,
|
||||
}
|
||||
}
|
||||
133
vendor/maunium.net/go/mautrix/crypto/utils/utils.go
generated
vendored
Normal file
133
vendor/maunium.net/go/mautrix/crypto/utils/utils.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2020 Nikos Filippakis
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"maunium.net/go/mautrix/util/base58"
|
||||
)
|
||||
|
||||
const (
|
||||
// AESCTRKeyLength is the length of the AES256-CTR key used.
|
||||
AESCTRKeyLength = 32
|
||||
// AESCTRIVLength is the length of the AES256-CTR IV used.
|
||||
AESCTRIVLength = 16
|
||||
// HMACKeyLength is the length of the HMAC key used.
|
||||
HMACKeyLength = 32
|
||||
// SHAHashLength is the length of the SHA hash used.
|
||||
SHAHashLength = 32
|
||||
)
|
||||
|
||||
// XorA256CTR encrypts the input with the keystream generated by the AES256-CTR algorithm with the given arguments.
|
||||
func XorA256CTR(source []byte, key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byte) []byte {
|
||||
block, _ := aes.NewCipher(key[:])
|
||||
cipher.NewCTR(block, iv[:]).XORKeyStream(source, source)
|
||||
return source
|
||||
}
|
||||
|
||||
// GenAttachmentA256CTR generates a new random AES256-CTR key and IV suitable for encrypting attachments.
|
||||
func GenAttachmentA256CTR() (key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byte) {
|
||||
_, err := rand.Read(key[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// The last 8 bytes of the IV act as the counter in AES-CTR, which means they're left empty here
|
||||
_, err = rand.Read(iv[:8])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GenA256CTRIV generates a random IV for AES256-CTR with the last bit set to zero.
|
||||
func GenA256CTRIV() (iv [AESCTRIVLength]byte) {
|
||||
_, err := rand.Read(iv[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
iv[8] &= 0x7F
|
||||
return
|
||||
}
|
||||
|
||||
// DeriveKeysSHA256 derives an AES and a HMAC key from the given recovery key.
|
||||
func DeriveKeysSHA256(key []byte, name string) ([AESCTRKeyLength]byte, [HMACKeyLength]byte) {
|
||||
var zeroBytes [32]byte
|
||||
|
||||
derivedHkdf := hkdf.New(sha256.New, key[:], zeroBytes[:], []byte(name))
|
||||
|
||||
var aesKey [AESCTRKeyLength]byte
|
||||
var hmacKey [HMACKeyLength]byte
|
||||
derivedHkdf.Read(aesKey[:])
|
||||
derivedHkdf.Read(hmacKey[:])
|
||||
|
||||
return aesKey, hmacKey
|
||||
}
|
||||
|
||||
// PBKDF2SHA512 generates a key of the given bit-length using the given passphrase, salt and iteration count.
|
||||
func PBKDF2SHA512(password []byte, salt []byte, iters int, keyLenBits int) []byte {
|
||||
return pbkdf2.Key(password, salt, iters, keyLenBits/8, sha512.New)
|
||||
}
|
||||
|
||||
// DecodeBase58RecoveryKey recovers the secret storage from a recovery key.
|
||||
func DecodeBase58RecoveryKey(recoveryKey string) []byte {
|
||||
noSpaces := strings.ReplaceAll(recoveryKey, " ", "")
|
||||
decoded := base58.Decode(noSpaces)
|
||||
if len(decoded) != AESCTRKeyLength+3 { // AESCTRKeyLength bytes key and 3 bytes prefix / parity
|
||||
return nil
|
||||
}
|
||||
var parity byte
|
||||
for _, b := range decoded[:34] {
|
||||
parity ^= b
|
||||
}
|
||||
if parity != decoded[34] || decoded[0] != 0x8B || decoded[1] != 1 {
|
||||
return nil
|
||||
}
|
||||
return decoded[2:34]
|
||||
}
|
||||
|
||||
// EncodeBase58RecoveryKey recovers the secret storage from a recovery key.
|
||||
func EncodeBase58RecoveryKey(key []byte) string {
|
||||
var inputBytes [35]byte
|
||||
copy(inputBytes[2:34], key[:])
|
||||
inputBytes[0] = 0x8B
|
||||
inputBytes[1] = 1
|
||||
|
||||
var parity byte
|
||||
for _, b := range inputBytes[:34] {
|
||||
parity ^= b
|
||||
}
|
||||
inputBytes[34] = parity
|
||||
recoveryKey := base58.Encode(inputBytes[:])
|
||||
|
||||
var spacedKey string
|
||||
for i, c := range recoveryKey {
|
||||
if i > 0 && i%4 == 0 {
|
||||
spacedKey += " "
|
||||
}
|
||||
spacedKey += string(c)
|
||||
}
|
||||
return spacedKey
|
||||
}
|
||||
|
||||
// HMACSHA256B64 calculates the base64 of the SHA256 hmac of the input with the given key.
|
||||
func HMACSHA256B64(input []byte, hmacKey [HMACKeyLength]byte) string {
|
||||
h := hmac.New(sha256.New, hmacKey[:])
|
||||
h.Write(input)
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
154
vendor/maunium.net/go/mautrix/error.go
generated
vendored
Normal file
154
vendor/maunium.net/go/mautrix/error.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Common error codes from https://matrix.org/docs/spec/client_server/latest#api-standards
|
||||
//
|
||||
// Can be used with errors.Is() to check the response code without casting the error:
|
||||
//
|
||||
// err := client.Sync()
|
||||
// if errors.Is(err, MUnknownToken) {
|
||||
// // logout
|
||||
// }
|
||||
var (
|
||||
// Forbidden access, e.g. joining a room without permission, failed login.
|
||||
MForbidden = RespError{ErrCode: "M_FORBIDDEN"}
|
||||
// Unrecognized request, e.g. the endpoint does not exist or is not implemented.
|
||||
MUnrecognized = RespError{ErrCode: "M_UNRECOGNIZED"}
|
||||
// The access token specified was not recognised.
|
||||
MUnknownToken = RespError{ErrCode: "M_UNKNOWN_TOKEN"}
|
||||
// No access token was specified for the request.
|
||||
MMissingToken = RespError{ErrCode: "M_MISSING_TOKEN"}
|
||||
// Request contained valid JSON, but it was malformed in some way, e.g. missing required keys, invalid values for keys.
|
||||
MBadJSON = RespError{ErrCode: "M_BAD_JSON"}
|
||||
// Request did not contain valid JSON.
|
||||
MNotJSON = RespError{ErrCode: "M_NOT_JSON"}
|
||||
// No resource was found for this request.
|
||||
MNotFound = RespError{ErrCode: "M_NOT_FOUND"}
|
||||
// Too many requests have been sent in a short period of time. Wait a while then try again.
|
||||
MLimitExceeded = RespError{ErrCode: "M_LIMIT_EXCEEDED"}
|
||||
// The user ID associated with the request has been deactivated.
|
||||
// Typically for endpoints that prove authentication, such as /login.
|
||||
MUserDeactivated = RespError{ErrCode: "M_USER_DEACTIVATED"}
|
||||
// Encountered when trying to register a user ID which has been taken.
|
||||
MUserInUse = RespError{ErrCode: "M_USER_IN_USE"}
|
||||
// Encountered when trying to register a user ID which is not valid.
|
||||
MInvalidUsername = RespError{ErrCode: "M_INVALID_USERNAME"}
|
||||
// Sent when the room alias given to the createRoom API is already in use.
|
||||
MRoomInUse = RespError{ErrCode: "M_ROOM_IN_USE"}
|
||||
// The state change requested cannot be performed, such as attempting to unban a user who is not banned.
|
||||
MBadState = RespError{ErrCode: "M_BAD_STATE"}
|
||||
// The request or entity was too large.
|
||||
MTooLarge = RespError{ErrCode: "M_TOO_LARGE"}
|
||||
// The resource being requested is reserved by an application service, or the application service making the request has not created the resource.
|
||||
MExclusive = RespError{ErrCode: "M_EXCLUSIVE"}
|
||||
// The client's request to create a room used a room version that the server does not support.
|
||||
MUnsupportedRoomVersion = RespError{ErrCode: "M_UNSUPPORTED_ROOM_VERSION"}
|
||||
// The client attempted to join a room that has a version the server does not support.
|
||||
// Inspect the room_version property of the error response for the room's version.
|
||||
MIncompatibleRoomVersion = RespError{ErrCode: "M_INCOMPATIBLE_ROOM_VERSION"}
|
||||
// The client specified a parameter that has the wrong value.
|
||||
MInvalidParam = RespError{ErrCode: "M_INVALID_PARAM"}
|
||||
|
||||
MSC2659URLNotSet = RespError{ErrCode: "FI.MAU.MSC2659_URL_NOT_SET"}
|
||||
MSC2659BadStatus = RespError{ErrCode: "FI.MAU.MSC2659_BAD_STATUS"}
|
||||
MSC2659ConnectionTimeout = RespError{ErrCode: "FI.MAU.MSC2659_CONNECTION_TIMEOUT"}
|
||||
MSC2659ConnectionFailed = RespError{ErrCode: "FI.MAU.MSC2659_CONNECTION_FAILED"}
|
||||
)
|
||||
|
||||
// HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
|
||||
type HTTPError struct {
|
||||
Request *http.Request
|
||||
Response *http.Response
|
||||
ResponseBody string
|
||||
|
||||
WrappedError error
|
||||
RespError *RespError
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e HTTPError) Is(err error) bool {
|
||||
return (e.RespError != nil && errors.Is(e.RespError, err)) || (e.WrappedError != nil && errors.Is(e.WrappedError, err))
|
||||
}
|
||||
|
||||
func (e HTTPError) IsStatus(code int) bool {
|
||||
return e.Response != nil && e.Response.StatusCode == code
|
||||
}
|
||||
|
||||
func (e HTTPError) Error() string {
|
||||
if e.WrappedError != nil {
|
||||
return fmt.Sprintf("%s: %v", e.Message, e.WrappedError)
|
||||
} else if e.RespError != nil {
|
||||
return fmt.Sprintf("failed to %s %s: %s (HTTP %d): %s", e.Request.Method, e.Request.URL.Path,
|
||||
e.RespError.ErrCode, e.Response.StatusCode, e.RespError.Err)
|
||||
} else {
|
||||
msg := fmt.Sprintf("failed to %s %s: %s", e.Request.Method, e.Request.URL.Path, e.Response.Status)
|
||||
if len(e.ResponseBody) > 0 {
|
||||
msg = fmt.Sprintf("%s\n%s", msg, e.ResponseBody)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
}
|
||||
|
||||
func (e HTTPError) Unwrap() error {
|
||||
if e.WrappedError != nil {
|
||||
return e.WrappedError
|
||||
} else if e.RespError != nil {
|
||||
return *e.RespError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface.
|
||||
// See https://spec.matrix.org/v1.2/client-server-api/#api-standards
|
||||
type RespError struct {
|
||||
ErrCode string
|
||||
Err string
|
||||
ExtraData map[string]interface{}
|
||||
}
|
||||
|
||||
func (e *RespError) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, &e.ExtraData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.ErrCode, _ = e.ExtraData["errcode"].(string)
|
||||
e.Err, _ = e.ExtraData["error"].(string)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *RespError) MarshalJSON() ([]byte, error) {
|
||||
if e.ExtraData == nil {
|
||||
e.ExtraData = make(map[string]interface{})
|
||||
}
|
||||
e.ExtraData["errcode"] = e.ErrCode
|
||||
e.ExtraData["error"] = e.Err
|
||||
return json.Marshal(&e.ExtraData)
|
||||
}
|
||||
|
||||
// Error returns the errcode and error message.
|
||||
func (e RespError) Error() string {
|
||||
return e.ErrCode + ": " + e.Err
|
||||
}
|
||||
|
||||
func (e RespError) Is(err error) bool {
|
||||
e2, ok := err.(RespError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if e.ErrCode == "M_UNKNOWN" && e2.ErrCode == "M_UNKNOWN" {
|
||||
return e.Err == e2.Err
|
||||
}
|
||||
return e2.ErrCode == e.ErrCode
|
||||
}
|
||||
45
vendor/maunium.net/go/mautrix/event/accountdata.go
generated
vendored
Normal file
45
vendor/maunium.net/go/mautrix/event/accountdata.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// TagEventContent represents the content of a m.tag room account data event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mtag
|
||||
type TagEventContent struct {
|
||||
Tags Tags `json:"tags"`
|
||||
}
|
||||
|
||||
type Tags map[string]Tag
|
||||
|
||||
type Tag struct {
|
||||
Order json.Number `json:"order,omitempty"`
|
||||
}
|
||||
|
||||
// DirectChatsEventContent represents the content of a m.direct account data event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mdirect
|
||||
type DirectChatsEventContent map[id.UserID][]id.RoomID
|
||||
|
||||
// FullyReadEventContent represents the content of a m.fully_read account data event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mfully_read
|
||||
type FullyReadEventContent struct {
|
||||
EventID id.EventID `json:"event_id"`
|
||||
}
|
||||
|
||||
// IgnoredUserListEventContent represents the content of a m.ignored_user_list account data event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mignored_user_list
|
||||
type IgnoredUserListEventContent struct {
|
||||
IgnoredUsers map[id.UserID]IgnoredUser `json:"ignored_users"`
|
||||
}
|
||||
|
||||
type IgnoredUser struct {
|
||||
// This is an empty object
|
||||
}
|
||||
51
vendor/maunium.net/go/mautrix/event/beeper.go
generated
vendored
Normal file
51
vendor/maunium.net/go/mautrix/event/beeper.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type MessageStatusReason string
|
||||
|
||||
const (
|
||||
MessageStatusGenericError MessageStatusReason = "m.event_not_handled"
|
||||
MessageStatusUnsupported MessageStatusReason = "com.beeper.unsupported_event"
|
||||
MessageStatusUndecryptable MessageStatusReason = "com.beeper.undecryptable_event"
|
||||
MessageStatusTooOld MessageStatusReason = "m.event_too_old"
|
||||
MessageStatusNetworkError MessageStatusReason = "m.foreign_network_error"
|
||||
MessageStatusNoPermission MessageStatusReason = "m.no_permission"
|
||||
MessageStatusBridgeUnavailable MessageStatusReason = "m.bridge_unavailable"
|
||||
)
|
||||
|
||||
type MessageStatus string
|
||||
|
||||
const (
|
||||
MessageStatusSuccess MessageStatus = "SUCCESS"
|
||||
MessageStatusPending MessageStatus = "PENDING"
|
||||
MessageStatusRetriable MessageStatus = "FAIL_RETRIABLE"
|
||||
MessageStatusFail MessageStatus = "FAIL_PERMANENT"
|
||||
)
|
||||
|
||||
type BeeperMessageStatusEventContent struct {
|
||||
Network string `json:"network"`
|
||||
RelatesTo RelatesTo `json:"m.relates_to"`
|
||||
Status MessageStatus `json:"status"`
|
||||
Reason MessageStatusReason `json:"reason,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
LastRetry id.EventID `json:"last_retry,omitempty"`
|
||||
|
||||
MutateEventKey string `json:"mutate_event_key,omitempty"`
|
||||
}
|
||||
|
||||
type BeeperRetryMetadata struct {
|
||||
OriginalEventID id.EventID `json:"original_event_id"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
// last_retry is also present, but not used by bridges
|
||||
}
|
||||
506
vendor/maunium.net/go/mautrix/event/content.go
generated
vendored
Normal file
506
vendor/maunium.net/go/mautrix/event/content.go
generated
vendored
Normal file
@@ -0,0 +1,506 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// TypeMap is a mapping from event type to the content struct type.
|
||||
// This is used by Content.ParseRaw() for creating the correct type of struct.
|
||||
var TypeMap = map[Type]reflect.Type{
|
||||
StateMember: reflect.TypeOf(MemberEventContent{}),
|
||||
StatePowerLevels: reflect.TypeOf(PowerLevelsEventContent{}),
|
||||
StateCanonicalAlias: reflect.TypeOf(CanonicalAliasEventContent{}),
|
||||
StateRoomName: reflect.TypeOf(RoomNameEventContent{}),
|
||||
StateRoomAvatar: reflect.TypeOf(RoomAvatarEventContent{}),
|
||||
StateServerACL: reflect.TypeOf(ServerACLEventContent{}),
|
||||
StateTopic: reflect.TypeOf(TopicEventContent{}),
|
||||
StateTombstone: reflect.TypeOf(TombstoneEventContent{}),
|
||||
StateCreate: reflect.TypeOf(CreateEventContent{}),
|
||||
StateJoinRules: reflect.TypeOf(JoinRulesEventContent{}),
|
||||
StateHistoryVisibility: reflect.TypeOf(HistoryVisibilityEventContent{}),
|
||||
StateGuestAccess: reflect.TypeOf(GuestAccessEventContent{}),
|
||||
StatePinnedEvents: reflect.TypeOf(PinnedEventsEventContent{}),
|
||||
StatePolicyRoom: reflect.TypeOf(ModPolicyContent{}),
|
||||
StatePolicyServer: reflect.TypeOf(ModPolicyContent{}),
|
||||
StatePolicyUser: reflect.TypeOf(ModPolicyContent{}),
|
||||
StateEncryption: reflect.TypeOf(EncryptionEventContent{}),
|
||||
StateBridge: reflect.TypeOf(BridgeEventContent{}),
|
||||
StateHalfShotBridge: reflect.TypeOf(BridgeEventContent{}),
|
||||
StateSpaceParent: reflect.TypeOf(SpaceParentEventContent{}),
|
||||
StateSpaceChild: reflect.TypeOf(SpaceChildEventContent{}),
|
||||
StateInsertionMarker: reflect.TypeOf(InsertionMarkerContent{}),
|
||||
|
||||
EventMessage: reflect.TypeOf(MessageEventContent{}),
|
||||
EventSticker: reflect.TypeOf(MessageEventContent{}),
|
||||
EventEncrypted: reflect.TypeOf(EncryptedEventContent{}),
|
||||
EventRedaction: reflect.TypeOf(RedactionEventContent{}),
|
||||
EventReaction: reflect.TypeOf(ReactionEventContent{}),
|
||||
|
||||
BeeperMessageStatus: reflect.TypeOf(BeeperMessageStatusEventContent{}),
|
||||
|
||||
AccountDataRoomTags: reflect.TypeOf(TagEventContent{}),
|
||||
AccountDataDirectChats: reflect.TypeOf(DirectChatsEventContent{}),
|
||||
AccountDataFullyRead: reflect.TypeOf(FullyReadEventContent{}),
|
||||
AccountDataIgnoredUserList: reflect.TypeOf(IgnoredUserListEventContent{}),
|
||||
|
||||
EphemeralEventTyping: reflect.TypeOf(TypingEventContent{}),
|
||||
EphemeralEventReceipt: reflect.TypeOf(ReceiptEventContent{}),
|
||||
EphemeralEventPresence: reflect.TypeOf(PresenceEventContent{}),
|
||||
|
||||
InRoomVerificationStart: reflect.TypeOf(VerificationStartEventContent{}),
|
||||
InRoomVerificationReady: reflect.TypeOf(VerificationReadyEventContent{}),
|
||||
InRoomVerificationAccept: reflect.TypeOf(VerificationAcceptEventContent{}),
|
||||
InRoomVerificationKey: reflect.TypeOf(VerificationKeyEventContent{}),
|
||||
InRoomVerificationMAC: reflect.TypeOf(VerificationMacEventContent{}),
|
||||
InRoomVerificationCancel: reflect.TypeOf(VerificationCancelEventContent{}),
|
||||
|
||||
ToDeviceRoomKey: reflect.TypeOf(RoomKeyEventContent{}),
|
||||
ToDeviceForwardedRoomKey: reflect.TypeOf(ForwardedRoomKeyEventContent{}),
|
||||
ToDeviceRoomKeyRequest: reflect.TypeOf(RoomKeyRequestEventContent{}),
|
||||
ToDeviceEncrypted: reflect.TypeOf(EncryptedEventContent{}),
|
||||
ToDeviceRoomKeyWithheld: reflect.TypeOf(RoomKeyWithheldEventContent{}),
|
||||
ToDeviceDummy: reflect.TypeOf(DummyEventContent{}),
|
||||
|
||||
ToDeviceVerificationStart: reflect.TypeOf(VerificationStartEventContent{}),
|
||||
ToDeviceVerificationAccept: reflect.TypeOf(VerificationAcceptEventContent{}),
|
||||
ToDeviceVerificationKey: reflect.TypeOf(VerificationKeyEventContent{}),
|
||||
ToDeviceVerificationMAC: reflect.TypeOf(VerificationMacEventContent{}),
|
||||
ToDeviceVerificationCancel: reflect.TypeOf(VerificationCancelEventContent{}),
|
||||
ToDeviceVerificationRequest: reflect.TypeOf(VerificationRequestEventContent{}),
|
||||
|
||||
ToDeviceOrgMatrixRoomKeyWithheld: reflect.TypeOf(RoomKeyWithheldEventContent{}),
|
||||
|
||||
CallInvite: reflect.TypeOf(CallInviteEventContent{}),
|
||||
CallCandidates: reflect.TypeOf(CallCandidatesEventContent{}),
|
||||
CallAnswer: reflect.TypeOf(CallAnswerEventContent{}),
|
||||
CallReject: reflect.TypeOf(CallRejectEventContent{}),
|
||||
CallSelectAnswer: reflect.TypeOf(CallSelectAnswerEventContent{}),
|
||||
CallNegotiate: reflect.TypeOf(CallNegotiateEventContent{}),
|
||||
CallHangup: reflect.TypeOf(CallHangupEventContent{}),
|
||||
}
|
||||
|
||||
// Content stores the content of a Matrix event.
|
||||
//
|
||||
// By default, the raw JSON bytes are stored in VeryRaw and parsed into a map[string]interface{} in the Raw field.
|
||||
// Additionally, you can call ParseRaw with the correct event type to parse the (VeryRaw) content into a nicer struct,
|
||||
// which you can then access from Parsed or via the helper functions.
|
||||
//
|
||||
// When being marshaled into JSON, the data in Parsed will be marshaled first and then recursively merged
|
||||
// with the data in Raw. Values in Raw are preferred, but nested objects will be recursed into before merging,
|
||||
// rather than overriding the whole object with the one in Raw).
|
||||
// If one of them is nil, the only the other is used. If both (Parsed and Raw) are nil, VeryRaw is used instead.
|
||||
type Content struct {
|
||||
VeryRaw json.RawMessage
|
||||
Raw map[string]interface{}
|
||||
Parsed interface{}
|
||||
}
|
||||
|
||||
type Relatable interface {
|
||||
GetRelatesTo() *RelatesTo
|
||||
OptionalGetRelatesTo() *RelatesTo
|
||||
SetRelatesTo(rel *RelatesTo)
|
||||
}
|
||||
|
||||
func (content *Content) UnmarshalJSON(data []byte) error {
|
||||
content.VeryRaw = data
|
||||
err := json.Unmarshal(data, &content.Raw)
|
||||
return err
|
||||
}
|
||||
|
||||
func (content *Content) MarshalJSON() ([]byte, error) {
|
||||
if content.Raw == nil {
|
||||
if content.Parsed == nil {
|
||||
if content.VeryRaw == nil {
|
||||
return []byte("{}"), nil
|
||||
}
|
||||
return content.VeryRaw, nil
|
||||
}
|
||||
return json.Marshal(content.Parsed)
|
||||
} else if content.Parsed != nil {
|
||||
// TODO this whole thing is incredibly hacky
|
||||
// It needs to produce JSON, where:
|
||||
// * content.Parsed is applied after content.Raw
|
||||
// * MarshalJSON() is respected inside content.Parsed
|
||||
// * Custom field inside nested objects of content.Raw are preserved,
|
||||
// even if content.Parsed contains the higher-level objects.
|
||||
// * content.Raw is not modified
|
||||
|
||||
unparsed, err := json.Marshal(content.Parsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rawParsed map[string]interface{}
|
||||
err = json.Unmarshal(unparsed, &rawParsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output := make(map[string]interface{})
|
||||
for key, value := range content.Raw {
|
||||
output[key] = value
|
||||
}
|
||||
|
||||
mergeMaps(output, rawParsed)
|
||||
return json.Marshal(output)
|
||||
}
|
||||
return json.Marshal(content.Raw)
|
||||
}
|
||||
|
||||
// Deprecated: use errors.Is directly
|
||||
func IsUnsupportedContentType(err error) bool {
|
||||
return errors.Is(err, ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
var ErrContentAlreadyParsed = errors.New("content is already parsed")
|
||||
var ErrUnsupportedContentType = errors.New("unsupported event type")
|
||||
|
||||
func (content *Content) ParseRaw(evtType Type) error {
|
||||
if content.Parsed != nil {
|
||||
return ErrContentAlreadyParsed
|
||||
}
|
||||
structType, ok := TypeMap[evtType]
|
||||
if !ok {
|
||||
return fmt.Errorf("%w %s", ErrUnsupportedContentType, evtType.Repr())
|
||||
}
|
||||
content.Parsed = reflect.New(structType).Interface()
|
||||
return json.Unmarshal(content.VeryRaw, &content.Parsed)
|
||||
}
|
||||
|
||||
func mergeMaps(into, from map[string]interface{}) {
|
||||
for key, newValue := range from {
|
||||
existingValue, ok := into[key]
|
||||
if !ok {
|
||||
into[key] = newValue
|
||||
continue
|
||||
}
|
||||
existingValueMap, okEx := existingValue.(map[string]interface{})
|
||||
newValueMap, okNew := newValue.(map[string]interface{})
|
||||
if okEx && okNew {
|
||||
mergeMaps(existingValueMap, newValueMap)
|
||||
} else {
|
||||
into[key] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
gob.Register(&MemberEventContent{})
|
||||
gob.Register(&PowerLevelsEventContent{})
|
||||
gob.Register(&CanonicalAliasEventContent{})
|
||||
gob.Register(&EncryptionEventContent{})
|
||||
gob.Register(&BridgeEventContent{})
|
||||
gob.Register(&SpaceChildEventContent{})
|
||||
gob.Register(&SpaceParentEventContent{})
|
||||
gob.Register(&RoomNameEventContent{})
|
||||
gob.Register(&RoomAvatarEventContent{})
|
||||
gob.Register(&TopicEventContent{})
|
||||
gob.Register(&TombstoneEventContent{})
|
||||
gob.Register(&CreateEventContent{})
|
||||
gob.Register(&JoinRulesEventContent{})
|
||||
gob.Register(&HistoryVisibilityEventContent{})
|
||||
gob.Register(&GuestAccessEventContent{})
|
||||
gob.Register(&PinnedEventsEventContent{})
|
||||
gob.Register(&MessageEventContent{})
|
||||
gob.Register(&MessageEventContent{})
|
||||
gob.Register(&EncryptedEventContent{})
|
||||
gob.Register(&RedactionEventContent{})
|
||||
gob.Register(&ReactionEventContent{})
|
||||
gob.Register(&TagEventContent{})
|
||||
gob.Register(&DirectChatsEventContent{})
|
||||
gob.Register(&FullyReadEventContent{})
|
||||
gob.Register(&IgnoredUserListEventContent{})
|
||||
gob.Register(&TypingEventContent{})
|
||||
gob.Register(&ReceiptEventContent{})
|
||||
gob.Register(&PresenceEventContent{})
|
||||
gob.Register(&RoomKeyEventContent{})
|
||||
gob.Register(&ForwardedRoomKeyEventContent{})
|
||||
gob.Register(&RoomKeyRequestEventContent{})
|
||||
gob.Register(&RoomKeyWithheldEventContent{})
|
||||
}
|
||||
|
||||
// Helper cast functions below
|
||||
|
||||
func (content *Content) AsMember() *MemberEventContent {
|
||||
casted, ok := content.Parsed.(*MemberEventContent)
|
||||
if !ok {
|
||||
return &MemberEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsPowerLevels() *PowerLevelsEventContent {
|
||||
casted, ok := content.Parsed.(*PowerLevelsEventContent)
|
||||
if !ok {
|
||||
return &PowerLevelsEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsCanonicalAlias() *CanonicalAliasEventContent {
|
||||
casted, ok := content.Parsed.(*CanonicalAliasEventContent)
|
||||
if !ok {
|
||||
return &CanonicalAliasEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsRoomName() *RoomNameEventContent {
|
||||
casted, ok := content.Parsed.(*RoomNameEventContent)
|
||||
if !ok {
|
||||
return &RoomNameEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsRoomAvatar() *RoomAvatarEventContent {
|
||||
casted, ok := content.Parsed.(*RoomAvatarEventContent)
|
||||
if !ok {
|
||||
return &RoomAvatarEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsTopic() *TopicEventContent {
|
||||
casted, ok := content.Parsed.(*TopicEventContent)
|
||||
if !ok {
|
||||
return &TopicEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsTombstone() *TombstoneEventContent {
|
||||
casted, ok := content.Parsed.(*TombstoneEventContent)
|
||||
if !ok {
|
||||
return &TombstoneEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsCreate() *CreateEventContent {
|
||||
casted, ok := content.Parsed.(*CreateEventContent)
|
||||
if !ok {
|
||||
return &CreateEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsJoinRules() *JoinRulesEventContent {
|
||||
casted, ok := content.Parsed.(*JoinRulesEventContent)
|
||||
if !ok {
|
||||
return &JoinRulesEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsHistoryVisibility() *HistoryVisibilityEventContent {
|
||||
casted, ok := content.Parsed.(*HistoryVisibilityEventContent)
|
||||
if !ok {
|
||||
return &HistoryVisibilityEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsGuestAccess() *GuestAccessEventContent {
|
||||
casted, ok := content.Parsed.(*GuestAccessEventContent)
|
||||
if !ok {
|
||||
return &GuestAccessEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsPinnedEvents() *PinnedEventsEventContent {
|
||||
casted, ok := content.Parsed.(*PinnedEventsEventContent)
|
||||
if !ok {
|
||||
return &PinnedEventsEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsEncryption() *EncryptionEventContent {
|
||||
casted, ok := content.Parsed.(*EncryptionEventContent)
|
||||
if !ok {
|
||||
return &EncryptionEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsBridge() *BridgeEventContent {
|
||||
casted, ok := content.Parsed.(*BridgeEventContent)
|
||||
if !ok {
|
||||
return &BridgeEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsSpaceChild() *SpaceChildEventContent {
|
||||
casted, ok := content.Parsed.(*SpaceChildEventContent)
|
||||
if !ok {
|
||||
return &SpaceChildEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsSpaceParent() *SpaceParentEventContent {
|
||||
casted, ok := content.Parsed.(*SpaceParentEventContent)
|
||||
if !ok {
|
||||
return &SpaceParentEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsMessage() *MessageEventContent {
|
||||
casted, ok := content.Parsed.(*MessageEventContent)
|
||||
if !ok {
|
||||
return &MessageEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsEncrypted() *EncryptedEventContent {
|
||||
casted, ok := content.Parsed.(*EncryptedEventContent)
|
||||
if !ok {
|
||||
return &EncryptedEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsRedaction() *RedactionEventContent {
|
||||
casted, ok := content.Parsed.(*RedactionEventContent)
|
||||
if !ok {
|
||||
return &RedactionEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsReaction() *ReactionEventContent {
|
||||
casted, ok := content.Parsed.(*ReactionEventContent)
|
||||
if !ok {
|
||||
return &ReactionEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsTag() *TagEventContent {
|
||||
casted, ok := content.Parsed.(*TagEventContent)
|
||||
if !ok {
|
||||
return &TagEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsDirectChats() *DirectChatsEventContent {
|
||||
casted, ok := content.Parsed.(*DirectChatsEventContent)
|
||||
if !ok {
|
||||
return &DirectChatsEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsFullyRead() *FullyReadEventContent {
|
||||
casted, ok := content.Parsed.(*FullyReadEventContent)
|
||||
if !ok {
|
||||
return &FullyReadEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsIgnoredUserList() *IgnoredUserListEventContent {
|
||||
casted, ok := content.Parsed.(*IgnoredUserListEventContent)
|
||||
if !ok {
|
||||
return &IgnoredUserListEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsTyping() *TypingEventContent {
|
||||
casted, ok := content.Parsed.(*TypingEventContent)
|
||||
if !ok {
|
||||
return &TypingEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsReceipt() *ReceiptEventContent {
|
||||
casted, ok := content.Parsed.(*ReceiptEventContent)
|
||||
if !ok {
|
||||
return &ReceiptEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsPresence() *PresenceEventContent {
|
||||
casted, ok := content.Parsed.(*PresenceEventContent)
|
||||
if !ok {
|
||||
return &PresenceEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsRoomKey() *RoomKeyEventContent {
|
||||
casted, ok := content.Parsed.(*RoomKeyEventContent)
|
||||
if !ok {
|
||||
return &RoomKeyEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsForwardedRoomKey() *ForwardedRoomKeyEventContent {
|
||||
casted, ok := content.Parsed.(*ForwardedRoomKeyEventContent)
|
||||
if !ok {
|
||||
return &ForwardedRoomKeyEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsRoomKeyRequest() *RoomKeyRequestEventContent {
|
||||
casted, ok := content.Parsed.(*RoomKeyRequestEventContent)
|
||||
if !ok {
|
||||
return &RoomKeyRequestEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsRoomKeyWithheld() *RoomKeyWithheldEventContent {
|
||||
casted, ok := content.Parsed.(*RoomKeyWithheldEventContent)
|
||||
if !ok {
|
||||
return &RoomKeyWithheldEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsCallInvite() *CallInviteEventContent {
|
||||
casted, ok := content.Parsed.(*CallInviteEventContent)
|
||||
if !ok {
|
||||
return &CallInviteEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsCallCandidates() *CallCandidatesEventContent {
|
||||
casted, ok := content.Parsed.(*CallCandidatesEventContent)
|
||||
if !ok {
|
||||
return &CallCandidatesEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsCallAnswer() *CallAnswerEventContent {
|
||||
casted, ok := content.Parsed.(*CallAnswerEventContent)
|
||||
if !ok {
|
||||
return &CallAnswerEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsCallReject() *CallRejectEventContent {
|
||||
casted, ok := content.Parsed.(*CallRejectEventContent)
|
||||
if !ok {
|
||||
return &CallRejectEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsCallSelectAnswer() *CallSelectAnswerEventContent {
|
||||
casted, ok := content.Parsed.(*CallSelectAnswerEventContent)
|
||||
if !ok {
|
||||
return &CallSelectAnswerEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsCallNegotiate() *CallNegotiateEventContent {
|
||||
casted, ok := content.Parsed.(*CallNegotiateEventContent)
|
||||
if !ok {
|
||||
return &CallNegotiateEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsCallHangup() *CallHangupEventContent {
|
||||
casted, ok := content.Parsed.(*CallHangupEventContent)
|
||||
if !ok {
|
||||
return &CallHangupEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsModPolicy() *ModPolicyContent {
|
||||
casted, ok := content.Parsed.(*ModPolicyContent)
|
||||
if !ok {
|
||||
return &ModPolicyContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
149
vendor/maunium.net/go/mautrix/event/encryption.go
generated
vendored
Normal file
149
vendor/maunium.net/go/mautrix/event/encryption.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// EncryptionEventContent represents the content of a m.room.encryption state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomencryption
|
||||
type EncryptionEventContent struct {
|
||||
// The encryption algorithm to be used to encrypt messages sent in this room. Must be 'm.megolm.v1.aes-sha2'.
|
||||
Algorithm id.Algorithm `json:"algorithm"`
|
||||
// How long the session should be used before changing it. 604800000 (a week) is the recommended default.
|
||||
RotationPeriodMillis int64 `json:"rotation_period_ms,omitempty"`
|
||||
// How many messages should be sent before changing the session. 100 is the recommended default.
|
||||
RotationPeriodMessages int `json:"rotation_period_msgs,omitempty"`
|
||||
}
|
||||
|
||||
// EncryptedEventContent represents the content of a m.room.encrypted message event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomencrypted
|
||||
//
|
||||
// Note that sender_key and device_id are deprecated in Megolm events as of https://github.com/matrix-org/matrix-spec-proposals/pull/3700
|
||||
type EncryptedEventContent struct {
|
||||
Algorithm id.Algorithm `json:"algorithm"`
|
||||
SenderKey id.SenderKey `json:"sender_key,omitempty"`
|
||||
// Deprecated: Matrix v1.3
|
||||
DeviceID id.DeviceID `json:"device_id,omitempty"`
|
||||
// Only present for Megolm events
|
||||
SessionID id.SessionID `json:"session_id,omitempty"`
|
||||
|
||||
Ciphertext json.RawMessage `json:"ciphertext"`
|
||||
|
||||
MegolmCiphertext []byte `json:"-"`
|
||||
OlmCiphertext OlmCiphertexts `json:"-"`
|
||||
|
||||
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
|
||||
Mentions *Mentions `json:"m.mentions,omitempty"`
|
||||
}
|
||||
|
||||
type OlmCiphertexts map[id.Curve25519]struct {
|
||||
Body string `json:"body"`
|
||||
Type id.OlmMsgType `json:"type"`
|
||||
}
|
||||
|
||||
type serializableEncryptedEventContent EncryptedEventContent
|
||||
|
||||
func (content *EncryptedEventContent) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, (*serializableEncryptedEventContent)(content))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch content.Algorithm {
|
||||
case id.AlgorithmOlmV1:
|
||||
content.OlmCiphertext = make(OlmCiphertexts)
|
||||
return json.Unmarshal(content.Ciphertext, &content.OlmCiphertext)
|
||||
case id.AlgorithmMegolmV1:
|
||||
if len(content.Ciphertext) == 0 || content.Ciphertext[0] != '"' || content.Ciphertext[len(content.Ciphertext)-1] != '"' {
|
||||
return id.InputNotJSONString
|
||||
}
|
||||
content.MegolmCiphertext = content.Ciphertext[1 : len(content.Ciphertext)-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (content *EncryptedEventContent) MarshalJSON() ([]byte, error) {
|
||||
var err error
|
||||
switch content.Algorithm {
|
||||
case id.AlgorithmOlmV1:
|
||||
content.Ciphertext, err = json.Marshal(content.OlmCiphertext)
|
||||
case id.AlgorithmMegolmV1:
|
||||
content.Ciphertext = make([]byte, len(content.MegolmCiphertext)+2)
|
||||
content.Ciphertext[0] = '"'
|
||||
content.Ciphertext[len(content.Ciphertext)-1] = '"'
|
||||
copy(content.Ciphertext[1:len(content.Ciphertext)-1], content.MegolmCiphertext)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal((*serializableEncryptedEventContent)(content))
|
||||
}
|
||||
|
||||
// RoomKeyEventContent represents the content of a m.room_key to_device event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroom_key
|
||||
type RoomKeyEventContent struct {
|
||||
Algorithm id.Algorithm `json:"algorithm"`
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
SessionID id.SessionID `json:"session_id"`
|
||||
SessionKey string `json:"session_key"`
|
||||
}
|
||||
|
||||
// ForwardedRoomKeyEventContent represents the content of a m.forwarded_room_key to_device event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mforwarded_room_key
|
||||
type ForwardedRoomKeyEventContent struct {
|
||||
RoomKeyEventContent
|
||||
SenderKey id.SenderKey `json:"sender_key"`
|
||||
SenderClaimedKey id.Ed25519 `json:"sender_claimed_ed25519_key"`
|
||||
ForwardingKeyChain []string `json:"forwarding_curve25519_key_chain"`
|
||||
}
|
||||
|
||||
type KeyRequestAction string
|
||||
|
||||
const (
|
||||
KeyRequestActionRequest = "request"
|
||||
KeyRequestActionCancel = "request_cancellation"
|
||||
)
|
||||
|
||||
// RoomKeyRequestEventContent represents the content of a m.room_key_request to_device event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroom_key_request
|
||||
type RoomKeyRequestEventContent struct {
|
||||
Body RequestedKeyInfo `json:"body"`
|
||||
Action KeyRequestAction `json:"action"`
|
||||
RequestingDeviceID id.DeviceID `json:"requesting_device_id"`
|
||||
RequestID string `json:"request_id"`
|
||||
}
|
||||
|
||||
type RequestedKeyInfo struct {
|
||||
Algorithm id.Algorithm `json:"algorithm"`
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
SenderKey id.SenderKey `json:"sender_key"`
|
||||
SessionID id.SessionID `json:"session_id"`
|
||||
}
|
||||
|
||||
type RoomKeyWithheldCode string
|
||||
|
||||
const (
|
||||
RoomKeyWithheldBlacklisted RoomKeyWithheldCode = "m.blacklisted"
|
||||
RoomKeyWithheldUnverified RoomKeyWithheldCode = "m.unverified"
|
||||
RoomKeyWithheldUnauthorized RoomKeyWithheldCode = "m.unauthorised"
|
||||
RoomKeyWithheldUnavailable RoomKeyWithheldCode = "m.unavailable"
|
||||
RoomKeyWithheldNoOlmSession RoomKeyWithheldCode = "m.no_olm"
|
||||
)
|
||||
|
||||
type RoomKeyWithheldEventContent struct {
|
||||
RoomID id.RoomID `json:"room_id,omitempty"`
|
||||
Algorithm id.Algorithm `json:"algorithm"`
|
||||
SessionID id.SessionID `json:"session_id,omitempty"`
|
||||
SenderKey id.SenderKey `json:"sender_key"`
|
||||
Code RoomKeyWithheldCode `json:"code"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
type DummyEventContent struct{}
|
||||
140
vendor/maunium.net/go/mautrix/event/ephemeral.go
generated
vendored
Normal file
140
vendor/maunium.net/go/mautrix/event/ephemeral.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// TypingEventContent represents the content of a m.typing ephemeral event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mtyping
|
||||
type TypingEventContent struct {
|
||||
UserIDs []id.UserID `json:"user_ids"`
|
||||
}
|
||||
|
||||
// ReceiptEventContent represents the content of a m.receipt ephemeral event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mreceipt
|
||||
type ReceiptEventContent map[id.EventID]Receipts
|
||||
|
||||
func (rec ReceiptEventContent) Set(evtID id.EventID, receiptType ReceiptType, userID id.UserID, receipt ReadReceipt) {
|
||||
rec.GetOrCreate(evtID).GetOrCreate(receiptType).Set(userID, receipt)
|
||||
}
|
||||
|
||||
func (rec ReceiptEventContent) GetOrCreate(evt id.EventID) Receipts {
|
||||
receipts, ok := rec[evt]
|
||||
if !ok {
|
||||
receipts = make(Receipts)
|
||||
rec[evt] = receipts
|
||||
}
|
||||
return receipts
|
||||
}
|
||||
|
||||
type ReceiptType string
|
||||
|
||||
const (
|
||||
ReceiptTypeRead ReceiptType = "m.read"
|
||||
ReceiptTypeReadPrivate ReceiptType = "m.read.private"
|
||||
)
|
||||
|
||||
type Receipts map[ReceiptType]UserReceipts
|
||||
|
||||
func (rps Receipts) GetOrCreate(receiptType ReceiptType) UserReceipts {
|
||||
read, ok := rps[receiptType]
|
||||
if !ok {
|
||||
read = make(UserReceipts)
|
||||
rps[receiptType] = read
|
||||
}
|
||||
return read
|
||||
}
|
||||
|
||||
type UserReceipts map[id.UserID]ReadReceipt
|
||||
|
||||
func (ur UserReceipts) Set(userID id.UserID, receipt ReadReceipt) {
|
||||
ur[userID] = receipt
|
||||
}
|
||||
|
||||
type ThreadID = id.EventID
|
||||
|
||||
const ReadReceiptThreadMain ThreadID = "main"
|
||||
|
||||
type ReadReceipt struct {
|
||||
Timestamp time.Time
|
||||
|
||||
// Thread ID for thread-specific read receipts from MSC3771
|
||||
ThreadID ThreadID
|
||||
|
||||
// Extra contains any unknown fields in the read receipt event.
|
||||
// Most servers don't allow clients to set them, so this will be empty in most cases.
|
||||
Extra map[string]interface{}
|
||||
}
|
||||
|
||||
func (rr *ReadReceipt) UnmarshalJSON(data []byte) error {
|
||||
// Hacky compatibility hack against crappy clients that send double-encoded read receipts.
|
||||
// TODO is this actually needed? clients can't currently set custom content in receipts 🤔
|
||||
if data[0] == '"' && data[len(data)-1] == '"' {
|
||||
var strData string
|
||||
err := json.Unmarshal(data, &strData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = []byte(strData)
|
||||
}
|
||||
|
||||
var parsed map[string]interface{}
|
||||
err := json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
threadID, _ := parsed["thread_id"].(string)
|
||||
ts, tsOK := parsed["ts"].(float64)
|
||||
delete(parsed, "thread_id")
|
||||
delete(parsed, "ts")
|
||||
*rr = ReadReceipt{
|
||||
ThreadID: ThreadID(threadID),
|
||||
Extra: parsed,
|
||||
}
|
||||
if tsOK {
|
||||
rr.Timestamp = time.UnixMilli(int64(ts))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr ReadReceipt) MarshalJSON() ([]byte, error) {
|
||||
data := rr.Extra
|
||||
if data == nil {
|
||||
data = make(map[string]interface{})
|
||||
}
|
||||
if rr.ThreadID != "" {
|
||||
data["thread_id"] = rr.ThreadID
|
||||
}
|
||||
if !rr.Timestamp.IsZero() {
|
||||
data["ts"] = rr.Timestamp.UnixMilli()
|
||||
}
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
type Presence string
|
||||
|
||||
const (
|
||||
PresenceOnline Presence = "online"
|
||||
PresenceOffline Presence = "offline"
|
||||
PresenceUnavailable Presence = "unavailable"
|
||||
)
|
||||
|
||||
// PresenceEventContent represents the content of a m.presence ephemeral event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mpresence
|
||||
type PresenceEventContent struct {
|
||||
Presence Presence `json:"presence"`
|
||||
Displayname string `json:"displayname,omitempty"`
|
||||
AvatarURL id.ContentURIString `json:"avatar_url,omitempty"`
|
||||
LastActiveAgo int64 `json:"last_active_ago,omitempty"`
|
||||
CurrentlyActive bool `json:"currently_active,omitempty"`
|
||||
StatusMessage string `json:"status_msg,omitempty"`
|
||||
}
|
||||
149
vendor/maunium.net/go/mautrix/event/events.go
generated
vendored
Normal file
149
vendor/maunium.net/go/mautrix/event/events.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// Event represents a single Matrix event.
|
||||
type Event struct {
|
||||
StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
|
||||
Sender id.UserID `json:"sender,omitempty"` // The user ID of the sender of the event
|
||||
Type Type `json:"type"` // The event type
|
||||
Timestamp int64 `json:"origin_server_ts,omitempty"` // The unix timestamp when this message was sent by the origin server
|
||||
ID id.EventID `json:"event_id,omitempty"` // The unique ID of this event
|
||||
RoomID id.RoomID `json:"room_id,omitempty"` // The room the event was sent to. May be nil (e.g. for presence)
|
||||
Content Content `json:"content"` // The JSON content of the event.
|
||||
Redacts id.EventID `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event
|
||||
Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver.
|
||||
|
||||
Mautrix MautrixInfo `json:"-"`
|
||||
|
||||
ToUserID id.UserID `json:"to_user_id,omitempty"` // The user ID that the to-device event was sent to. Only present in MSC2409 appservice transactions.
|
||||
ToDeviceID id.DeviceID `json:"to_device_id,omitempty"` // The device ID that the to-device event was sent to. Only present in MSC2409 appservice transactions.
|
||||
}
|
||||
|
||||
type eventForMarshaling struct {
|
||||
StateKey *string `json:"state_key,omitempty"`
|
||||
Sender id.UserID `json:"sender,omitempty"`
|
||||
Type Type `json:"type"`
|
||||
Timestamp int64 `json:"origin_server_ts,omitempty"`
|
||||
ID id.EventID `json:"event_id,omitempty"`
|
||||
RoomID id.RoomID `json:"room_id,omitempty"`
|
||||
Content Content `json:"content"`
|
||||
Redacts id.EventID `json:"redacts,omitempty"`
|
||||
Unsigned *Unsigned `json:"unsigned,omitempty"`
|
||||
|
||||
PrevContent *Content `json:"prev_content,omitempty"`
|
||||
ReplacesState *id.EventID `json:"replaces_state,omitempty"`
|
||||
|
||||
ToUserID id.UserID `json:"to_user_id,omitempty"`
|
||||
ToDeviceID id.DeviceID `json:"to_device_id,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals the event, including moving prev_content from the top level to inside unsigned.
|
||||
func (evt *Event) UnmarshalJSON(data []byte) error {
|
||||
var efm eventForMarshaling
|
||||
err := json.Unmarshal(data, &efm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
evt.StateKey = efm.StateKey
|
||||
evt.Sender = efm.Sender
|
||||
evt.Type = efm.Type
|
||||
evt.Timestamp = efm.Timestamp
|
||||
evt.ID = efm.ID
|
||||
evt.RoomID = efm.RoomID
|
||||
evt.Content = efm.Content
|
||||
evt.Redacts = efm.Redacts
|
||||
if efm.Unsigned != nil {
|
||||
evt.Unsigned = *efm.Unsigned
|
||||
}
|
||||
if efm.PrevContent != nil && evt.Unsigned.PrevContent == nil {
|
||||
evt.Unsigned.PrevContent = efm.PrevContent
|
||||
}
|
||||
if efm.ReplacesState != nil && *efm.ReplacesState != "" && evt.Unsigned.ReplacesState == "" {
|
||||
evt.Unsigned.ReplacesState = *efm.ReplacesState
|
||||
}
|
||||
evt.ToUserID = efm.ToUserID
|
||||
evt.ToDeviceID = efm.ToDeviceID
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON marshals the event, including omitting the unsigned field if it's empty.
|
||||
//
|
||||
// This is necessary because Unsigned is not a pointer (for convenience reasons),
|
||||
// and encoding/json doesn't know how to check if a non-pointer struct is empty.
|
||||
//
|
||||
// TODO(tulir): maybe it makes more sense to make Unsigned a pointer and make an easy and safe way to access it?
|
||||
func (evt *Event) MarshalJSON() ([]byte, error) {
|
||||
unsigned := &evt.Unsigned
|
||||
if unsigned.IsEmpty() {
|
||||
unsigned = nil
|
||||
}
|
||||
return json.Marshal(&eventForMarshaling{
|
||||
StateKey: evt.StateKey,
|
||||
Sender: evt.Sender,
|
||||
Type: evt.Type,
|
||||
Timestamp: evt.Timestamp,
|
||||
ID: evt.ID,
|
||||
RoomID: evt.RoomID,
|
||||
Content: evt.Content,
|
||||
Redacts: evt.Redacts,
|
||||
Unsigned: unsigned,
|
||||
ToUserID: evt.ToUserID,
|
||||
ToDeviceID: evt.ToDeviceID,
|
||||
})
|
||||
}
|
||||
|
||||
type MautrixInfo struct {
|
||||
TrustState id.TrustState
|
||||
ForwardedKeys bool
|
||||
WasEncrypted bool
|
||||
TrustSource *id.Device
|
||||
|
||||
ReceivedAt time.Time
|
||||
DecryptionDuration time.Duration
|
||||
|
||||
CheckpointSent bool
|
||||
}
|
||||
|
||||
func (evt *Event) GetStateKey() string {
|
||||
if evt.StateKey != nil {
|
||||
return *evt.StateKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type StrippedState struct {
|
||||
Content Content `json:"content"`
|
||||
Type Type `json:"type"`
|
||||
StateKey string `json:"state_key"`
|
||||
Sender id.UserID `json:"sender"`
|
||||
}
|
||||
|
||||
type Unsigned struct {
|
||||
PrevContent *Content `json:"prev_content,omitempty"`
|
||||
PrevSender id.UserID `json:"prev_sender,omitempty"`
|
||||
ReplacesState id.EventID `json:"replaces_state,omitempty"`
|
||||
Age int64 `json:"age,omitempty"`
|
||||
TransactionID string `json:"transaction_id,omitempty"`
|
||||
Relations *Relations `json:"m.relations,omitempty"`
|
||||
RedactedBecause *Event `json:"redacted_because,omitempty"`
|
||||
InviteRoomState []StrippedState `json:"invite_room_state,omitempty"`
|
||||
|
||||
BeeperHSOrder int64 `json:"com.beeper.hs.order,omitempty"`
|
||||
}
|
||||
|
||||
func (us *Unsigned) IsEmpty() bool {
|
||||
return us.PrevContent == nil && us.PrevSender == "" && us.ReplacesState == "" && us.Age == 0 &&
|
||||
us.TransactionID == "" && us.RedactedBecause == nil && us.InviteRoomState == nil && us.Relations == nil
|
||||
}
|
||||
53
vendor/maunium.net/go/mautrix/event/member.go
generated
vendored
Normal file
53
vendor/maunium.net/go/mautrix/event/member.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// Membership is an enum specifying the membership state of a room member.
|
||||
type Membership string
|
||||
|
||||
func (ms Membership) IsInviteOrJoin() bool {
|
||||
return ms == MembershipJoin || ms == MembershipInvite
|
||||
}
|
||||
|
||||
func (ms Membership) IsLeaveOrBan() bool {
|
||||
return ms == MembershipLeave || ms == MembershipBan
|
||||
}
|
||||
|
||||
// The allowed membership states as specified in spec section 10.5.5.
|
||||
const (
|
||||
MembershipJoin Membership = "join"
|
||||
MembershipLeave Membership = "leave"
|
||||
MembershipInvite Membership = "invite"
|
||||
MembershipBan Membership = "ban"
|
||||
MembershipKnock Membership = "knock"
|
||||
)
|
||||
|
||||
// MemberEventContent represents the content of a m.room.member state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroommember
|
||||
type MemberEventContent struct {
|
||||
Membership Membership `json:"membership"`
|
||||
AvatarURL id.ContentURIString `json:"avatar_url,omitempty"`
|
||||
Displayname string `json:"displayname,omitempty"`
|
||||
IsDirect bool `json:"is_direct,omitempty"`
|
||||
ThirdPartyInvite *ThirdPartyInvite `json:"third_party_invite,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
type ThirdPartyInvite struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
Signed struct {
|
||||
Token string `json:"token"`
|
||||
Signatures json.RawMessage `json:"signatures"`
|
||||
MXID string `json:"mxid"`
|
||||
}
|
||||
}
|
||||
276
vendor/maunium.net/go/mautrix/event/message.go
generated
vendored
Normal file
276
vendor/maunium.net/go/mautrix/event/message.go
generated
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"maunium.net/go/mautrix/crypto/attachment"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// MessageType is the sub-type of a m.room.message event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroommessage-msgtypes
|
||||
type MessageType string
|
||||
|
||||
// Msgtypes
|
||||
const (
|
||||
MsgText MessageType = "m.text"
|
||||
MsgEmote MessageType = "m.emote"
|
||||
MsgNotice MessageType = "m.notice"
|
||||
MsgImage MessageType = "m.image"
|
||||
MsgLocation MessageType = "m.location"
|
||||
MsgVideo MessageType = "m.video"
|
||||
MsgAudio MessageType = "m.audio"
|
||||
MsgFile MessageType = "m.file"
|
||||
|
||||
MsgVerificationRequest MessageType = "m.key.verification.request"
|
||||
)
|
||||
|
||||
// Format specifies the format of the formatted_body in m.room.message events.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroommessage-msgtypes
|
||||
type Format string
|
||||
|
||||
// Message formats
|
||||
const (
|
||||
FormatHTML Format = "org.matrix.custom.html"
|
||||
)
|
||||
|
||||
// RedactionEventContent represents the content of a m.room.redaction message event.
|
||||
//
|
||||
// The redacted event ID is still at the top level, but will move in a future room version.
|
||||
// See https://github.com/matrix-org/matrix-doc/pull/2244 and https://github.com/matrix-org/matrix-doc/pull/2174
|
||||
//
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomredaction
|
||||
type RedactionEventContent struct {
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// ReactionEventContent represents the content of a m.reaction message event.
|
||||
// This is not yet in a spec release, see https://github.com/matrix-org/matrix-doc/pull/1849
|
||||
type ReactionEventContent struct {
|
||||
RelatesTo RelatesTo `json:"m.relates_to"`
|
||||
}
|
||||
|
||||
func (content *ReactionEventContent) GetRelatesTo() *RelatesTo {
|
||||
return &content.RelatesTo
|
||||
}
|
||||
|
||||
func (content *ReactionEventContent) OptionalGetRelatesTo() *RelatesTo {
|
||||
return &content.RelatesTo
|
||||
}
|
||||
|
||||
func (content *ReactionEventContent) SetRelatesTo(rel *RelatesTo) {
|
||||
content.RelatesTo = *rel
|
||||
}
|
||||
|
||||
// MessageEventContent represents the content of a m.room.message event.
|
||||
//
|
||||
// It is also used to represent m.sticker events, as they are equivalent to m.room.message
|
||||
// with the exception of the msgtype field.
|
||||
//
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroommessage
|
||||
type MessageEventContent struct {
|
||||
// Base m.room.message fields
|
||||
MsgType MessageType `json:"msgtype,omitempty"`
|
||||
Body string `json:"body"`
|
||||
|
||||
// Extra fields for text types
|
||||
Format Format `json:"format,omitempty"`
|
||||
FormattedBody string `json:"formatted_body,omitempty"`
|
||||
|
||||
// Extra field for m.location
|
||||
GeoURI string `json:"geo_uri,omitempty"`
|
||||
|
||||
// Extra fields for media types
|
||||
URL id.ContentURIString `json:"url,omitempty"`
|
||||
Info *FileInfo `json:"info,omitempty"`
|
||||
File *EncryptedFileInfo `json:"file,omitempty"`
|
||||
|
||||
FileName string `json:"filename,omitempty"`
|
||||
|
||||
Mentions *Mentions `json:"m.mentions,omitempty"`
|
||||
UnstableMentions *Mentions `json:"org.matrix.msc3952.mentions,omitempty"`
|
||||
|
||||
// Edits and relations
|
||||
NewContent *MessageEventContent `json:"m.new_content,omitempty"`
|
||||
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
|
||||
|
||||
// In-room verification
|
||||
To id.UserID `json:"to,omitempty"`
|
||||
FromDevice id.DeviceID `json:"from_device,omitempty"`
|
||||
Methods []VerificationMethod `json:"methods,omitempty"`
|
||||
|
||||
replyFallbackRemoved bool
|
||||
|
||||
MessageSendRetry *BeeperRetryMetadata `json:"com.beeper.message_send_retry,omitempty"`
|
||||
}
|
||||
|
||||
func (content *MessageEventContent) GetRelatesTo() *RelatesTo {
|
||||
if content.RelatesTo == nil {
|
||||
content.RelatesTo = &RelatesTo{}
|
||||
}
|
||||
return content.RelatesTo
|
||||
}
|
||||
|
||||
func (content *MessageEventContent) OptionalGetRelatesTo() *RelatesTo {
|
||||
return content.RelatesTo
|
||||
}
|
||||
|
||||
func (content *MessageEventContent) SetRelatesTo(rel *RelatesTo) {
|
||||
content.RelatesTo = rel
|
||||
}
|
||||
|
||||
func (content *MessageEventContent) SetEdit(original id.EventID) {
|
||||
newContent := *content
|
||||
content.NewContent = &newContent
|
||||
content.RelatesTo = (&RelatesTo{}).SetReplace(original)
|
||||
if content.MsgType == MsgText || content.MsgType == MsgNotice {
|
||||
content.Body = "* " + content.Body
|
||||
if content.Format == FormatHTML && len(content.FormattedBody) > 0 {
|
||||
content.FormattedBody = "* " + content.FormattedBody
|
||||
}
|
||||
// If the message is long, remove most of the useless edit fallback to avoid event size issues.
|
||||
if len(content.Body) > 10000 {
|
||||
content.FormattedBody = ""
|
||||
content.Format = ""
|
||||
content.Body = content.Body[:50] + "[edit fallback cut…]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TextToHTML(text string) string {
|
||||
return strings.ReplaceAll(html.EscapeString(text), "\n", "<br/>")
|
||||
}
|
||||
|
||||
func (content *MessageEventContent) EnsureHasHTML() {
|
||||
if len(content.FormattedBody) == 0 || content.Format != FormatHTML {
|
||||
content.FormattedBody = TextToHTML(content.Body)
|
||||
content.Format = FormatHTML
|
||||
}
|
||||
}
|
||||
|
||||
func (content *MessageEventContent) GetFile() *EncryptedFileInfo {
|
||||
if content.File == nil {
|
||||
content.File = &EncryptedFileInfo{}
|
||||
}
|
||||
return content.File
|
||||
}
|
||||
|
||||
func (content *MessageEventContent) GetInfo() *FileInfo {
|
||||
if content.Info == nil {
|
||||
content.Info = &FileInfo{}
|
||||
}
|
||||
return content.Info
|
||||
}
|
||||
|
||||
type Mentions struct {
|
||||
UserIDs []id.UserID `json:"user_ids,omitempty"`
|
||||
Room bool `json:"room,omitempty"`
|
||||
}
|
||||
|
||||
type EncryptedFileInfo struct {
|
||||
attachment.EncryptedFile
|
||||
URL id.ContentURIString `json:"url"`
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
MimeType string `json:"mimetype,omitempty"`
|
||||
ThumbnailInfo *FileInfo `json:"thumbnail_info,omitempty"`
|
||||
ThumbnailURL id.ContentURIString `json:"thumbnail_url,omitempty"`
|
||||
ThumbnailFile *EncryptedFileInfo `json:"thumbnail_file,omitempty"`
|
||||
Width int `json:"-"`
|
||||
Height int `json:"-"`
|
||||
Duration int `json:"-"`
|
||||
Size int `json:"-"`
|
||||
}
|
||||
|
||||
type serializableFileInfo struct {
|
||||
MimeType string `json:"mimetype,omitempty"`
|
||||
ThumbnailInfo *serializableFileInfo `json:"thumbnail_info,omitempty"`
|
||||
ThumbnailURL id.ContentURIString `json:"thumbnail_url,omitempty"`
|
||||
ThumbnailFile *EncryptedFileInfo `json:"thumbnail_file,omitempty"`
|
||||
|
||||
Width json.Number `json:"w,omitempty"`
|
||||
Height json.Number `json:"h,omitempty"`
|
||||
Duration json.Number `json:"duration,omitempty"`
|
||||
Size json.Number `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
func (sfi *serializableFileInfo) CopyFrom(fileInfo *FileInfo) *serializableFileInfo {
|
||||
if fileInfo == nil {
|
||||
return nil
|
||||
}
|
||||
*sfi = serializableFileInfo{
|
||||
MimeType: fileInfo.MimeType,
|
||||
ThumbnailURL: fileInfo.ThumbnailURL,
|
||||
ThumbnailInfo: (&serializableFileInfo{}).CopyFrom(fileInfo.ThumbnailInfo),
|
||||
ThumbnailFile: fileInfo.ThumbnailFile,
|
||||
}
|
||||
if fileInfo.Width > 0 {
|
||||
sfi.Width = json.Number(strconv.Itoa(fileInfo.Width))
|
||||
}
|
||||
if fileInfo.Height > 0 {
|
||||
sfi.Height = json.Number(strconv.Itoa(fileInfo.Height))
|
||||
}
|
||||
if fileInfo.Size > 0 {
|
||||
sfi.Size = json.Number(strconv.Itoa(fileInfo.Size))
|
||||
|
||||
}
|
||||
if fileInfo.Duration > 0 {
|
||||
sfi.Duration = json.Number(strconv.Itoa(int(fileInfo.Duration)))
|
||||
}
|
||||
return sfi
|
||||
}
|
||||
|
||||
func (sfi *serializableFileInfo) CopyTo(fileInfo *FileInfo) {
|
||||
*fileInfo = FileInfo{
|
||||
Width: numberToInt(sfi.Width),
|
||||
Height: numberToInt(sfi.Height),
|
||||
Size: numberToInt(sfi.Size),
|
||||
Duration: numberToInt(sfi.Duration),
|
||||
MimeType: sfi.MimeType,
|
||||
ThumbnailURL: sfi.ThumbnailURL,
|
||||
ThumbnailFile: sfi.ThumbnailFile,
|
||||
}
|
||||
if sfi.ThumbnailInfo != nil {
|
||||
fileInfo.ThumbnailInfo = &FileInfo{}
|
||||
sfi.ThumbnailInfo.CopyTo(fileInfo.ThumbnailInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func (fileInfo *FileInfo) UnmarshalJSON(data []byte) error {
|
||||
sfi := &serializableFileInfo{}
|
||||
if err := json.Unmarshal(data, sfi); err != nil {
|
||||
return err
|
||||
}
|
||||
sfi.CopyTo(fileInfo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fileInfo *FileInfo) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal((&serializableFileInfo{}).CopyFrom(fileInfo))
|
||||
}
|
||||
|
||||
func numberToInt(val json.Number) int {
|
||||
f64, _ := val.Float64()
|
||||
if f64 > 0 {
|
||||
return int(f64)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (fileInfo *FileInfo) GetThumbnailInfo() *FileInfo {
|
||||
if fileInfo.ThumbnailInfo == nil {
|
||||
fileInfo.ThumbnailInfo = &FileInfo{}
|
||||
}
|
||||
return fileInfo.ThumbnailInfo
|
||||
}
|
||||
149
vendor/maunium.net/go/mautrix/event/powerlevels.go
generated
vendored
Normal file
149
vendor/maunium.net/go/mautrix/event/powerlevels.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// PowerLevelsEventContent represents the content of a m.room.power_levels state event content.
|
||||
// https://spec.matrix.org/v1.5/client-server-api/#mroompower_levels
|
||||
type PowerLevelsEventContent struct {
|
||||
usersLock sync.RWMutex
|
||||
Users map[id.UserID]int `json:"users,omitempty"`
|
||||
UsersDefault int `json:"users_default,omitempty"`
|
||||
|
||||
eventsLock sync.RWMutex
|
||||
Events map[string]int `json:"events,omitempty"`
|
||||
EventsDefault int `json:"events_default,omitempty"`
|
||||
|
||||
Notifications *NotificationPowerLevels `json:"notifications,omitempty"`
|
||||
|
||||
StateDefaultPtr *int `json:"state_default,omitempty"`
|
||||
|
||||
InvitePtr *int `json:"invite,omitempty"`
|
||||
KickPtr *int `json:"kick,omitempty"`
|
||||
BanPtr *int `json:"ban,omitempty"`
|
||||
RedactPtr *int `json:"redact,omitempty"`
|
||||
HistoricalPtr *int `json:"historical,omitempty"`
|
||||
}
|
||||
|
||||
type NotificationPowerLevels struct {
|
||||
RoomPtr *int `json:"room,omitempty"`
|
||||
}
|
||||
|
||||
func (npl *NotificationPowerLevels) Room() int {
|
||||
if npl != nil && npl.RoomPtr != nil {
|
||||
return *npl.RoomPtr
|
||||
}
|
||||
return 50
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) Invite() int {
|
||||
if pl.InvitePtr != nil {
|
||||
return *pl.InvitePtr
|
||||
}
|
||||
return 50
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) Kick() int {
|
||||
if pl.KickPtr != nil {
|
||||
return *pl.KickPtr
|
||||
}
|
||||
return 50
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) Ban() int {
|
||||
if pl.BanPtr != nil {
|
||||
return *pl.BanPtr
|
||||
}
|
||||
return 50
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) Redact() int {
|
||||
if pl.RedactPtr != nil {
|
||||
return *pl.RedactPtr
|
||||
}
|
||||
return 50
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) Historical() int {
|
||||
if pl.HistoricalPtr != nil {
|
||||
return *pl.HistoricalPtr
|
||||
}
|
||||
return 100
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) StateDefault() int {
|
||||
if pl.StateDefaultPtr != nil {
|
||||
return *pl.StateDefaultPtr
|
||||
}
|
||||
return 50
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) GetUserLevel(userID id.UserID) int {
|
||||
pl.usersLock.RLock()
|
||||
defer pl.usersLock.RUnlock()
|
||||
level, ok := pl.Users[userID]
|
||||
if !ok {
|
||||
return pl.UsersDefault
|
||||
}
|
||||
return level
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) SetUserLevel(userID id.UserID, level int) {
|
||||
pl.usersLock.Lock()
|
||||
defer pl.usersLock.Unlock()
|
||||
if level == pl.UsersDefault {
|
||||
delete(pl.Users, userID)
|
||||
} else {
|
||||
pl.Users[userID] = level
|
||||
}
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) EnsureUserLevel(userID id.UserID, level int) bool {
|
||||
existingLevel := pl.GetUserLevel(userID)
|
||||
if existingLevel != level {
|
||||
pl.SetUserLevel(userID, level)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) GetEventLevel(eventType Type) int {
|
||||
pl.eventsLock.RLock()
|
||||
defer pl.eventsLock.RUnlock()
|
||||
level, ok := pl.Events[eventType.String()]
|
||||
if !ok {
|
||||
if eventType.IsState() {
|
||||
return pl.StateDefault()
|
||||
}
|
||||
return pl.EventsDefault
|
||||
}
|
||||
return level
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) SetEventLevel(eventType Type, level int) {
|
||||
pl.eventsLock.Lock()
|
||||
defer pl.eventsLock.Unlock()
|
||||
if (eventType.IsState() && level == pl.StateDefault()) || (!eventType.IsState() && level == pl.EventsDefault) {
|
||||
delete(pl.Events, eventType.String())
|
||||
} else {
|
||||
pl.Events[eventType.String()] = level
|
||||
}
|
||||
}
|
||||
|
||||
func (pl *PowerLevelsEventContent) EnsureEventLevel(eventType Type, level int) bool {
|
||||
existingLevel := pl.GetEventLevel(eventType)
|
||||
if existingLevel != level {
|
||||
pl.SetEventLevel(eventType, level)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
234
vendor/maunium.net/go/mautrix/event/relations.go
generated
vendored
Normal file
234
vendor/maunium.net/go/mautrix/event/relations.go
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type RelationType string
|
||||
|
||||
const (
|
||||
RelReplace RelationType = "m.replace"
|
||||
RelReference RelationType = "m.reference"
|
||||
RelAnnotation RelationType = "m.annotation"
|
||||
RelThread RelationType = "m.thread"
|
||||
)
|
||||
|
||||
type RelatesTo struct {
|
||||
Type RelationType `json:"rel_type,omitempty"`
|
||||
EventID id.EventID `json:"event_id,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
|
||||
InReplyTo *InReplyTo `json:"m.in_reply_to,omitempty"`
|
||||
IsFallingBack bool `json:"is_falling_back,omitempty"`
|
||||
}
|
||||
|
||||
type InReplyTo struct {
|
||||
EventID id.EventID `json:"event_id,omitempty"`
|
||||
|
||||
UnstableRoomID id.RoomID `json:"room_id,omitempty"`
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) Copy() *RelatesTo {
|
||||
if rel == nil {
|
||||
return nil
|
||||
}
|
||||
cp := *rel
|
||||
return &cp
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) GetReplaceID() id.EventID {
|
||||
if rel != nil && rel.Type == RelReplace {
|
||||
return rel.EventID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) GetReferenceID() id.EventID {
|
||||
if rel != nil && rel.Type == RelReference {
|
||||
return rel.EventID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) GetThreadParent() id.EventID {
|
||||
if rel != nil && rel.Type == RelThread {
|
||||
return rel.EventID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) GetReplyTo() id.EventID {
|
||||
if rel != nil && rel.InReplyTo != nil {
|
||||
return rel.InReplyTo.EventID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) GetNonFallbackReplyTo() id.EventID {
|
||||
if rel != nil && rel.InReplyTo != nil && !rel.IsFallingBack {
|
||||
return rel.InReplyTo.EventID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) GetAnnotationID() id.EventID {
|
||||
if rel != nil && rel.Type == RelAnnotation {
|
||||
return rel.EventID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) GetAnnotationKey() string {
|
||||
if rel != nil && rel.Type == RelAnnotation {
|
||||
return rel.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) SetReplace(mxid id.EventID) *RelatesTo {
|
||||
rel.Type = RelReplace
|
||||
rel.EventID = mxid
|
||||
return rel
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) SetReplyTo(mxid id.EventID) *RelatesTo {
|
||||
rel.InReplyTo = &InReplyTo{EventID: mxid}
|
||||
rel.IsFallingBack = false
|
||||
return rel
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) SetThread(mxid, fallback id.EventID) *RelatesTo {
|
||||
rel.Type = RelThread
|
||||
rel.EventID = mxid
|
||||
if fallback != "" && rel.GetReplyTo() == "" {
|
||||
rel.SetReplyTo(fallback)
|
||||
rel.IsFallingBack = true
|
||||
}
|
||||
return rel
|
||||
}
|
||||
|
||||
func (rel *RelatesTo) SetAnnotation(mxid id.EventID, key string) *RelatesTo {
|
||||
rel.Type = RelAnnotation
|
||||
rel.EventID = mxid
|
||||
rel.Key = key
|
||||
return rel
|
||||
}
|
||||
|
||||
type RelationChunkItem struct {
|
||||
Type RelationType `json:"type"`
|
||||
EventID string `json:"event_id,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
Count int `json:"count,omitempty"`
|
||||
}
|
||||
|
||||
type RelationChunk struct {
|
||||
Chunk []RelationChunkItem `json:"chunk"`
|
||||
|
||||
Limited bool `json:"limited"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type AnnotationChunk struct {
|
||||
RelationChunk
|
||||
Map map[string]int `json:"-"`
|
||||
}
|
||||
|
||||
type serializableAnnotationChunk AnnotationChunk
|
||||
|
||||
func (ac *AnnotationChunk) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, (*serializableAnnotationChunk)(ac)); err != nil {
|
||||
return err
|
||||
}
|
||||
ac.Map = make(map[string]int)
|
||||
for _, item := range ac.Chunk {
|
||||
if item.Key != "" {
|
||||
ac.Map[item.Key] += item.Count
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac *AnnotationChunk) Serialize() RelationChunk {
|
||||
ac.Chunk = make([]RelationChunkItem, len(ac.Map))
|
||||
i := 0
|
||||
for key, count := range ac.Map {
|
||||
ac.Chunk[i] = RelationChunkItem{
|
||||
Type: RelAnnotation,
|
||||
Key: key,
|
||||
Count: count,
|
||||
}
|
||||
i++
|
||||
}
|
||||
return ac.RelationChunk
|
||||
}
|
||||
|
||||
type EventIDChunk struct {
|
||||
RelationChunk
|
||||
List []string `json:"-"`
|
||||
}
|
||||
|
||||
type serializableEventIDChunk EventIDChunk
|
||||
|
||||
func (ec *EventIDChunk) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, (*serializableEventIDChunk)(ec)); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range ec.Chunk {
|
||||
ec.List = append(ec.List, item.EventID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ec *EventIDChunk) Serialize(typ RelationType) RelationChunk {
|
||||
ec.Chunk = make([]RelationChunkItem, len(ec.List))
|
||||
for i, eventID := range ec.List {
|
||||
ec.Chunk[i] = RelationChunkItem{
|
||||
Type: typ,
|
||||
EventID: eventID,
|
||||
}
|
||||
}
|
||||
return ec.RelationChunk
|
||||
}
|
||||
|
||||
type Relations struct {
|
||||
Raw map[RelationType]RelationChunk `json:"-"`
|
||||
|
||||
Annotations AnnotationChunk `json:"m.annotation,omitempty"`
|
||||
References EventIDChunk `json:"m.reference,omitempty"`
|
||||
Replaces EventIDChunk `json:"m.replace,omitempty"`
|
||||
}
|
||||
|
||||
type serializableRelations Relations
|
||||
|
||||
func (relations *Relations) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, &relations.Raw); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, (*serializableRelations)(relations))
|
||||
}
|
||||
|
||||
func (relations *Relations) MarshalJSON() ([]byte, error) {
|
||||
if relations.Raw == nil {
|
||||
relations.Raw = make(map[RelationType]RelationChunk)
|
||||
}
|
||||
relations.Raw[RelAnnotation] = relations.Annotations.Serialize()
|
||||
relations.Raw[RelReference] = relations.References.Serialize(RelReference)
|
||||
relations.Raw[RelReplace] = relations.Replaces.Serialize(RelReplace)
|
||||
for key, item := range relations.Raw {
|
||||
if !item.Limited {
|
||||
item.Count = len(item.Chunk)
|
||||
}
|
||||
if item.Count == 0 {
|
||||
delete(relations.Raw, key)
|
||||
}
|
||||
}
|
||||
return json.Marshal(relations.Raw)
|
||||
}
|
||||
100
vendor/maunium.net/go/mautrix/event/reply.go
generated
vendored
Normal file
100
vendor/maunium.net/go/mautrix/event/reply.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
var HTMLReplyFallbackRegex = regexp.MustCompile(`^<mx-reply>[\s\S]+?</mx-reply>`)
|
||||
|
||||
func TrimReplyFallbackHTML(html string) string {
|
||||
return HTMLReplyFallbackRegex.ReplaceAllString(html, "")
|
||||
}
|
||||
|
||||
func TrimReplyFallbackText(text string) string {
|
||||
if !strings.HasPrefix(text, "> ") || !strings.Contains(text, "\n") {
|
||||
return text
|
||||
}
|
||||
|
||||
lines := strings.Split(text, "\n")
|
||||
for len(lines) > 0 && strings.HasPrefix(lines[0], "> ") {
|
||||
lines = lines[1:]
|
||||
}
|
||||
return strings.TrimSpace(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func (content *MessageEventContent) RemoveReplyFallback() {
|
||||
if len(content.RelatesTo.GetReplyTo()) > 0 && !content.replyFallbackRemoved {
|
||||
if content.Format == FormatHTML {
|
||||
content.FormattedBody = TrimReplyFallbackHTML(content.FormattedBody)
|
||||
}
|
||||
content.Body = TrimReplyFallbackText(content.Body)
|
||||
content.replyFallbackRemoved = true
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: RelatesTo methods are nil-safe, so RelatesTo.GetReplyTo can be used directly
|
||||
func (content *MessageEventContent) GetReplyTo() id.EventID {
|
||||
return content.RelatesTo.GetReplyTo()
|
||||
}
|
||||
|
||||
const ReplyFormat = `<mx-reply><blockquote><a href="https://matrix.to/#/%s/%s">In reply to</a> <a href="https://matrix.to/#/%s">%s</a><br>%s</blockquote></mx-reply>`
|
||||
|
||||
func (evt *Event) GenerateReplyFallbackHTML() string {
|
||||
parsedContent, ok := evt.Content.Parsed.(*MessageEventContent)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
parsedContent.RemoveReplyFallback()
|
||||
body := parsedContent.FormattedBody
|
||||
if len(body) == 0 {
|
||||
body = strings.ReplaceAll(html.EscapeString(parsedContent.Body), "\n", "<br/>")
|
||||
}
|
||||
|
||||
senderDisplayName := evt.Sender
|
||||
|
||||
return fmt.Sprintf(ReplyFormat, evt.RoomID, evt.ID, evt.Sender, senderDisplayName, body)
|
||||
}
|
||||
|
||||
func (evt *Event) GenerateReplyFallbackText() string {
|
||||
parsedContent, ok := evt.Content.Parsed.(*MessageEventContent)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
parsedContent.RemoveReplyFallback()
|
||||
body := parsedContent.Body
|
||||
lines := strings.Split(strings.TrimSpace(body), "\n")
|
||||
firstLine, lines := lines[0], lines[1:]
|
||||
|
||||
senderDisplayName := evt.Sender
|
||||
|
||||
var fallbackText strings.Builder
|
||||
_, _ = fmt.Fprintf(&fallbackText, "> <%s> %s", senderDisplayName, firstLine)
|
||||
for _, line := range lines {
|
||||
_, _ = fmt.Fprintf(&fallbackText, "\n> %s", line)
|
||||
}
|
||||
fallbackText.WriteString("\n\n")
|
||||
return fallbackText.String()
|
||||
}
|
||||
|
||||
func (content *MessageEventContent) SetReply(inReplyTo *Event) {
|
||||
content.RelatesTo = (&RelatesTo{}).SetReplyTo(inReplyTo.ID)
|
||||
|
||||
if content.MsgType == MsgText || content.MsgType == MsgNotice {
|
||||
content.EnsureHasHTML()
|
||||
content.FormattedBody = inReplyTo.GenerateReplyFallbackHTML() + content.FormattedBody
|
||||
content.Body = inReplyTo.GenerateReplyFallbackText() + content.Body
|
||||
content.replyFallbackRemoved = false
|
||||
}
|
||||
}
|
||||
176
vendor/maunium.net/go/mautrix/event/state.go
generated
vendored
Normal file
176
vendor/maunium.net/go/mautrix/event/state.go
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// CanonicalAliasEventContent represents the content of a m.room.canonical_alias state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomcanonical_alias
|
||||
type CanonicalAliasEventContent struct {
|
||||
Alias id.RoomAlias `json:"alias"`
|
||||
AltAliases []id.RoomAlias `json:"alt_aliases,omitempty"`
|
||||
}
|
||||
|
||||
// RoomNameEventContent represents the content of a m.room.name state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomname
|
||||
type RoomNameEventContent struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// RoomAvatarEventContent represents the content of a m.room.avatar state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomavatar
|
||||
type RoomAvatarEventContent struct {
|
||||
URL id.ContentURI `json:"url"`
|
||||
Info *FileInfo `json:"info,omitempty"`
|
||||
}
|
||||
|
||||
// ServerACLEventContent represents the content of a m.room.server_acl state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#server-access-control-lists-acls-for-rooms
|
||||
type ServerACLEventContent struct {
|
||||
Allow []string `json:"allow,omitempty"`
|
||||
AllowIPLiterals bool `json:"allow_ip_literals"`
|
||||
Deny []string `json:"deny,omitempty"`
|
||||
}
|
||||
|
||||
// TopicEventContent represents the content of a m.room.topic state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomtopic
|
||||
type TopicEventContent struct {
|
||||
Topic string `json:"topic"`
|
||||
}
|
||||
|
||||
// TombstoneEventContent represents the content of a m.room.tombstone state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomtombstone
|
||||
type TombstoneEventContent struct {
|
||||
Body string `json:"body"`
|
||||
ReplacementRoom id.RoomID `json:"replacement_room"`
|
||||
}
|
||||
|
||||
type Predecessor struct {
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
EventID id.EventID `json:"event_id"`
|
||||
}
|
||||
|
||||
// CreateEventContent represents the content of a m.room.create state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomcreate
|
||||
type CreateEventContent struct {
|
||||
Type RoomType `json:"type,omitempty"`
|
||||
Creator id.UserID `json:"creator,omitempty"`
|
||||
Federate bool `json:"m.federate,omitempty"`
|
||||
RoomVersion string `json:"room_version,omitempty"`
|
||||
Predecessor *Predecessor `json:"predecessor,omitempty"`
|
||||
}
|
||||
|
||||
// JoinRule specifies how open a room is to new members.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomjoin_rules
|
||||
type JoinRule string
|
||||
|
||||
const (
|
||||
JoinRulePublic JoinRule = "public"
|
||||
JoinRuleKnock JoinRule = "knock"
|
||||
JoinRuleInvite JoinRule = "invite"
|
||||
JoinRuleRestricted JoinRule = "restricted"
|
||||
JoinRulePrivate JoinRule = "private"
|
||||
)
|
||||
|
||||
// JoinRulesEventContent represents the content of a m.room.join_rules state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomjoin_rules
|
||||
type JoinRulesEventContent struct {
|
||||
JoinRule JoinRule `json:"join_rule"`
|
||||
Allow []JoinRuleAllow `json:"allow,omitempty"`
|
||||
}
|
||||
|
||||
type JoinRuleAllowType string
|
||||
|
||||
const (
|
||||
JoinRuleAllowRoomMembership JoinRuleAllowType = "m.room_membership"
|
||||
)
|
||||
|
||||
type JoinRuleAllow struct {
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
Type JoinRuleAllowType `json:"type"`
|
||||
}
|
||||
|
||||
// PinnedEventsEventContent represents the content of a m.room.pinned_events state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroompinned_events
|
||||
type PinnedEventsEventContent struct {
|
||||
Pinned []id.EventID `json:"pinned"`
|
||||
}
|
||||
|
||||
// HistoryVisibility specifies who can see new messages.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomhistory_visibility
|
||||
type HistoryVisibility string
|
||||
|
||||
const (
|
||||
HistoryVisibilityInvited HistoryVisibility = "invited"
|
||||
HistoryVisibilityJoined HistoryVisibility = "joined"
|
||||
HistoryVisibilityShared HistoryVisibility = "shared"
|
||||
HistoryVisibilityWorldReadable HistoryVisibility = "world_readable"
|
||||
)
|
||||
|
||||
// HistoryVisibilityEventContent represents the content of a m.room.history_visibility state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomhistory_visibility
|
||||
type HistoryVisibilityEventContent struct {
|
||||
HistoryVisibility HistoryVisibility `json:"history_visibility"`
|
||||
}
|
||||
|
||||
// GuestAccess specifies whether or not guest accounts can join.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomguest_access
|
||||
type GuestAccess string
|
||||
|
||||
const (
|
||||
GuestAccessCanJoin GuestAccess = "can_join"
|
||||
GuestAccessForbidden GuestAccess = "forbidden"
|
||||
)
|
||||
|
||||
// GuestAccessEventContent represents the content of a m.room.guest_access state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mroomguest_access
|
||||
type GuestAccessEventContent struct {
|
||||
GuestAccess GuestAccess `json:"guest_access"`
|
||||
}
|
||||
|
||||
type BridgeInfoSection struct {
|
||||
ID string `json:"id"`
|
||||
DisplayName string `json:"displayname,omitempty"`
|
||||
AvatarURL id.ContentURIString `json:"avatar_url,omitempty"`
|
||||
ExternalURL string `json:"external_url,omitempty"`
|
||||
}
|
||||
|
||||
// BridgeEventContent represents the content of a m.bridge state event.
|
||||
// https://github.com/matrix-org/matrix-doc/pull/2346
|
||||
type BridgeEventContent struct {
|
||||
BridgeBot id.UserID `json:"bridgebot"`
|
||||
Creator id.UserID `json:"creator,omitempty"`
|
||||
Protocol BridgeInfoSection `json:"protocol"`
|
||||
Network *BridgeInfoSection `json:"network,omitempty"`
|
||||
Channel BridgeInfoSection `json:"channel"`
|
||||
}
|
||||
|
||||
type SpaceChildEventContent struct {
|
||||
Via []string `json:"via,omitempty"`
|
||||
Order string `json:"order,omitempty"`
|
||||
Suggested bool `json:"suggested,omitempty"`
|
||||
}
|
||||
|
||||
type SpaceParentEventContent struct {
|
||||
Via []string `json:"via,omitempty"`
|
||||
Canonical bool `json:"canonical,omitempty"`
|
||||
}
|
||||
|
||||
// ModPolicyContent represents the content of a m.room.rule.user, m.room.rule.room, and m.room.rule.server state event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#moderation-policy-lists
|
||||
type ModPolicyContent struct {
|
||||
Entity string `json:"entity"`
|
||||
Reason string `json:"reason"`
|
||||
Recommendation string `json:"recommendation"`
|
||||
}
|
||||
|
||||
type InsertionMarkerContent struct {
|
||||
InsertionID id.EventID `json:"org.matrix.msc2716.marker.insertion"`
|
||||
Timestamp int64 `json:"com.beeper.timestamp,omitempty"`
|
||||
}
|
||||
256
vendor/maunium.net/go/mautrix/event/type.go
generated
vendored
Normal file
256
vendor/maunium.net/go/mautrix/event/type.go
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type RoomType string
|
||||
|
||||
const (
|
||||
RoomTypeDefault RoomType = ""
|
||||
RoomTypeSpace RoomType = "m.space"
|
||||
)
|
||||
|
||||
type TypeClass int
|
||||
|
||||
func (tc TypeClass) Name() string {
|
||||
switch tc {
|
||||
case MessageEventType:
|
||||
return "message"
|
||||
case StateEventType:
|
||||
return "state"
|
||||
case EphemeralEventType:
|
||||
return "ephemeral"
|
||||
case AccountDataEventType:
|
||||
return "account data"
|
||||
case ToDeviceEventType:
|
||||
return "to-device"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// Unknown events
|
||||
UnknownEventType TypeClass = iota
|
||||
// Normal message events
|
||||
MessageEventType
|
||||
// State events
|
||||
StateEventType
|
||||
// Ephemeral events
|
||||
EphemeralEventType
|
||||
// Account data events
|
||||
AccountDataEventType
|
||||
// Device-to-device events
|
||||
ToDeviceEventType
|
||||
)
|
||||
|
||||
type Type struct {
|
||||
Type string
|
||||
Class TypeClass
|
||||
}
|
||||
|
||||
func NewEventType(name string) Type {
|
||||
evtType := Type{Type: name}
|
||||
evtType.Class = evtType.GuessClass()
|
||||
return evtType
|
||||
}
|
||||
|
||||
func (et *Type) IsState() bool {
|
||||
return et.Class == StateEventType
|
||||
}
|
||||
|
||||
func (et *Type) IsEphemeral() bool {
|
||||
return et.Class == EphemeralEventType
|
||||
}
|
||||
|
||||
func (et *Type) IsAccountData() bool {
|
||||
return et.Class == AccountDataEventType
|
||||
}
|
||||
|
||||
func (et *Type) IsToDevice() bool {
|
||||
return et.Class == ToDeviceEventType
|
||||
}
|
||||
|
||||
func (et *Type) IsInRoomVerification() bool {
|
||||
switch et.Type {
|
||||
case InRoomVerificationStart.Type, InRoomVerificationReady.Type, InRoomVerificationAccept.Type,
|
||||
InRoomVerificationKey.Type, InRoomVerificationMAC.Type, InRoomVerificationCancel.Type:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (et *Type) IsCall() bool {
|
||||
switch et.Type {
|
||||
case CallInvite.Type, CallCandidates.Type, CallAnswer.Type, CallReject.Type, CallSelectAnswer.Type,
|
||||
CallNegotiate.Type, CallHangup.Type:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (et *Type) IsCustom() bool {
|
||||
return !strings.HasPrefix(et.Type, "m.")
|
||||
}
|
||||
|
||||
func (et *Type) GuessClass() TypeClass {
|
||||
switch et.Type {
|
||||
case StateAliases.Type, StateCanonicalAlias.Type, StateCreate.Type, StateJoinRules.Type, StateMember.Type,
|
||||
StatePowerLevels.Type, StateRoomName.Type, StateRoomAvatar.Type, StateServerACL.Type, StateTopic.Type,
|
||||
StatePinnedEvents.Type, StateTombstone.Type, StateEncryption.Type, StateBridge.Type, StateHalfShotBridge.Type,
|
||||
StateSpaceParent.Type, StateSpaceChild.Type, StatePolicyRoom.Type, StatePolicyServer.Type, StatePolicyUser.Type,
|
||||
StateInsertionMarker.Type:
|
||||
return StateEventType
|
||||
case EphemeralEventReceipt.Type, EphemeralEventTyping.Type, EphemeralEventPresence.Type:
|
||||
return EphemeralEventType
|
||||
case AccountDataDirectChats.Type, AccountDataPushRules.Type, AccountDataRoomTags.Type,
|
||||
AccountDataSecretStorageKey.Type, AccountDataSecretStorageDefaultKey.Type,
|
||||
AccountDataCrossSigningMaster.Type, AccountDataCrossSigningSelf.Type, AccountDataCrossSigningUser.Type:
|
||||
return AccountDataEventType
|
||||
case EventRedaction.Type, EventMessage.Type, EventEncrypted.Type, EventReaction.Type, EventSticker.Type,
|
||||
InRoomVerificationStart.Type, InRoomVerificationReady.Type, InRoomVerificationAccept.Type,
|
||||
InRoomVerificationKey.Type, InRoomVerificationMAC.Type, InRoomVerificationCancel.Type,
|
||||
CallInvite.Type, CallCandidates.Type, CallAnswer.Type, CallReject.Type, CallSelectAnswer.Type,
|
||||
CallNegotiate.Type, CallHangup.Type, BeeperMessageStatus.Type:
|
||||
return MessageEventType
|
||||
case ToDeviceRoomKey.Type, ToDeviceRoomKeyRequest.Type, ToDeviceForwardedRoomKey.Type, ToDeviceRoomKeyWithheld.Type:
|
||||
return ToDeviceEventType
|
||||
default:
|
||||
return UnknownEventType
|
||||
}
|
||||
}
|
||||
|
||||
func (et *Type) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, &et.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
et.Class = et.GuessClass()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (et *Type) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&et.Type)
|
||||
}
|
||||
|
||||
func (et Type) UnmarshalText(data []byte) error {
|
||||
et.Type = string(data)
|
||||
et.Class = et.GuessClass()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (et Type) MarshalText() ([]byte, error) {
|
||||
return []byte(et.Type), nil
|
||||
}
|
||||
|
||||
func (et *Type) String() string {
|
||||
return et.Type
|
||||
}
|
||||
|
||||
func (et *Type) Repr() string {
|
||||
return fmt.Sprintf("%s (%s)", et.Type, et.Class.Name())
|
||||
}
|
||||
|
||||
// State events
|
||||
var (
|
||||
StateAliases = Type{"m.room.aliases", StateEventType}
|
||||
StateCanonicalAlias = Type{"m.room.canonical_alias", StateEventType}
|
||||
StateCreate = Type{"m.room.create", StateEventType}
|
||||
StateJoinRules = Type{"m.room.join_rules", StateEventType}
|
||||
StateHistoryVisibility = Type{"m.room.history_visibility", StateEventType}
|
||||
StateGuestAccess = Type{"m.room.guest_access", StateEventType}
|
||||
StateMember = Type{"m.room.member", StateEventType}
|
||||
StatePowerLevels = Type{"m.room.power_levels", StateEventType}
|
||||
StateRoomName = Type{"m.room.name", StateEventType}
|
||||
StateTopic = Type{"m.room.topic", StateEventType}
|
||||
StateRoomAvatar = Type{"m.room.avatar", StateEventType}
|
||||
StatePinnedEvents = Type{"m.room.pinned_events", StateEventType}
|
||||
StateServerACL = Type{"m.room.server_acl", StateEventType}
|
||||
StateTombstone = Type{"m.room.tombstone", StateEventType}
|
||||
StatePolicyRoom = Type{"m.policy.rule.room", StateEventType}
|
||||
StatePolicyServer = Type{"m.policy.rule.server", StateEventType}
|
||||
StatePolicyUser = Type{"m.policy.rule.user", StateEventType}
|
||||
StateEncryption = Type{"m.room.encryption", StateEventType}
|
||||
StateBridge = Type{"m.bridge", StateEventType}
|
||||
StateHalfShotBridge = Type{"uk.half-shot.bridge", StateEventType}
|
||||
StateSpaceChild = Type{"m.space.child", StateEventType}
|
||||
StateSpaceParent = Type{"m.space.parent", StateEventType}
|
||||
StateInsertionMarker = Type{"org.matrix.msc2716.marker", StateEventType}
|
||||
)
|
||||
|
||||
// Message events
|
||||
var (
|
||||
EventRedaction = Type{"m.room.redaction", MessageEventType}
|
||||
EventMessage = Type{"m.room.message", MessageEventType}
|
||||
EventEncrypted = Type{"m.room.encrypted", MessageEventType}
|
||||
EventReaction = Type{"m.reaction", MessageEventType}
|
||||
EventSticker = Type{"m.sticker", MessageEventType}
|
||||
|
||||
InRoomVerificationStart = Type{"m.key.verification.start", MessageEventType}
|
||||
InRoomVerificationReady = Type{"m.key.verification.ready", MessageEventType}
|
||||
InRoomVerificationAccept = Type{"m.key.verification.accept", MessageEventType}
|
||||
InRoomVerificationKey = Type{"m.key.verification.key", MessageEventType}
|
||||
InRoomVerificationMAC = Type{"m.key.verification.mac", MessageEventType}
|
||||
InRoomVerificationCancel = Type{"m.key.verification.cancel", MessageEventType}
|
||||
|
||||
CallInvite = Type{"m.call.invite", MessageEventType}
|
||||
CallCandidates = Type{"m.call.candidates", MessageEventType}
|
||||
CallAnswer = Type{"m.call.answer", MessageEventType}
|
||||
CallReject = Type{"m.call.reject", MessageEventType}
|
||||
CallSelectAnswer = Type{"m.call.select_answer", MessageEventType}
|
||||
CallNegotiate = Type{"m.call.negotiate", MessageEventType}
|
||||
CallHangup = Type{"m.call.hangup", MessageEventType}
|
||||
|
||||
BeeperMessageStatus = Type{"com.beeper.message_send_status", MessageEventType}
|
||||
)
|
||||
|
||||
// Ephemeral events
|
||||
var (
|
||||
EphemeralEventReceipt = Type{"m.receipt", EphemeralEventType}
|
||||
EphemeralEventTyping = Type{"m.typing", EphemeralEventType}
|
||||
EphemeralEventPresence = Type{"m.presence", EphemeralEventType}
|
||||
)
|
||||
|
||||
// Account data events
|
||||
var (
|
||||
AccountDataDirectChats = Type{"m.direct", AccountDataEventType}
|
||||
AccountDataPushRules = Type{"m.push_rules", AccountDataEventType}
|
||||
AccountDataRoomTags = Type{"m.tag", AccountDataEventType}
|
||||
AccountDataFullyRead = Type{"m.fully_read", AccountDataEventType}
|
||||
AccountDataIgnoredUserList = Type{"m.ignored_user_list", AccountDataEventType}
|
||||
|
||||
AccountDataSecretStorageDefaultKey = Type{"m.secret_storage.default_key", AccountDataEventType}
|
||||
AccountDataSecretStorageKey = Type{"m.secret_storage.key", AccountDataEventType}
|
||||
AccountDataCrossSigningMaster = Type{"m.cross_signing.master", AccountDataEventType}
|
||||
AccountDataCrossSigningUser = Type{"m.cross_signing.user_signing", AccountDataEventType}
|
||||
AccountDataCrossSigningSelf = Type{"m.cross_signing.self_signing", AccountDataEventType}
|
||||
)
|
||||
|
||||
// Device-to-device events
|
||||
var (
|
||||
ToDeviceRoomKey = Type{"m.room_key", ToDeviceEventType}
|
||||
ToDeviceRoomKeyRequest = Type{"m.room_key_request", ToDeviceEventType}
|
||||
ToDeviceForwardedRoomKey = Type{"m.forwarded_room_key", ToDeviceEventType}
|
||||
ToDeviceEncrypted = Type{"m.room.encrypted", ToDeviceEventType}
|
||||
ToDeviceRoomKeyWithheld = Type{"m.room_key.withheld", ToDeviceEventType}
|
||||
ToDeviceDummy = Type{"m.dummy", ToDeviceEventType}
|
||||
ToDeviceVerificationRequest = Type{"m.key.verification.request", ToDeviceEventType}
|
||||
ToDeviceVerificationStart = Type{"m.key.verification.start", ToDeviceEventType}
|
||||
ToDeviceVerificationAccept = Type{"m.key.verification.accept", ToDeviceEventType}
|
||||
ToDeviceVerificationKey = Type{"m.key.verification.key", ToDeviceEventType}
|
||||
ToDeviceVerificationMAC = Type{"m.key.verification.mac", ToDeviceEventType}
|
||||
ToDeviceVerificationCancel = Type{"m.key.verification.cancel", ToDeviceEventType}
|
||||
|
||||
ToDeviceOrgMatrixRoomKeyWithheld = Type{"org.matrix.room_key.withheld", ToDeviceEventType}
|
||||
)
|
||||
307
vendor/maunium.net/go/mautrix/event/verification.go
generated
vendored
Normal file
307
vendor/maunium.net/go/mautrix/event/verification.go
generated
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
// Copyright (c) 2020 Nikos Filippakis
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type VerificationMethod string
|
||||
|
||||
const VerificationMethodSAS VerificationMethod = "m.sas.v1"
|
||||
|
||||
// VerificationRequestEventContent represents the content of a m.key.verification.request to_device event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mkeyverificationrequest
|
||||
type VerificationRequestEventContent struct {
|
||||
// The device ID which is initiating the request.
|
||||
FromDevice id.DeviceID `json:"from_device"`
|
||||
// An opaque identifier for the verification request. Must be unique with respect to the devices involved.
|
||||
TransactionID string `json:"transaction_id,omitempty"`
|
||||
// The verification methods supported by the sender.
|
||||
Methods []VerificationMethod `json:"methods"`
|
||||
// The POSIX timestamp in milliseconds for when the request was made.
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
// The user that the event is sent to for in-room verification.
|
||||
To id.UserID `json:"to,omitempty"`
|
||||
// Original event ID for in-room verification.
|
||||
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
|
||||
}
|
||||
|
||||
func (vrec *VerificationRequestEventContent) SupportsVerificationMethod(meth VerificationMethod) bool {
|
||||
for _, supportedMeth := range vrec.Methods {
|
||||
if supportedMeth == meth {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type KeyAgreementProtocol string
|
||||
|
||||
const (
|
||||
KeyAgreementCurve25519 KeyAgreementProtocol = "curve25519"
|
||||
KeyAgreementCurve25519HKDFSHA256 KeyAgreementProtocol = "curve25519-hkdf-sha256"
|
||||
)
|
||||
|
||||
type VerificationHashMethod string
|
||||
|
||||
const VerificationHashSHA256 VerificationHashMethod = "sha256"
|
||||
|
||||
type MACMethod string
|
||||
|
||||
const HKDFHMACSHA256 MACMethod = "hkdf-hmac-sha256"
|
||||
|
||||
type SASMethod string
|
||||
|
||||
const (
|
||||
SASDecimal SASMethod = "decimal"
|
||||
SASEmoji SASMethod = "emoji"
|
||||
)
|
||||
|
||||
// VerificationStartEventContent represents the content of a m.key.verification.start to_device event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mkeyverificationstartmsasv1
|
||||
type VerificationStartEventContent struct {
|
||||
// The device ID which is initiating the process.
|
||||
FromDevice id.DeviceID `json:"from_device"`
|
||||
// An opaque identifier for the verification process. Must be unique with respect to the devices involved.
|
||||
TransactionID string `json:"transaction_id,omitempty"`
|
||||
// The verification method to use.
|
||||
Method VerificationMethod `json:"method"`
|
||||
// The key agreement protocols the sending device understands.
|
||||
KeyAgreementProtocols []KeyAgreementProtocol `json:"key_agreement_protocols"`
|
||||
// The hash methods the sending device understands.
|
||||
Hashes []VerificationHashMethod `json:"hashes"`
|
||||
// The message authentication codes that the sending device understands.
|
||||
MessageAuthenticationCodes []MACMethod `json:"message_authentication_codes"`
|
||||
// The SAS methods the sending device (and the sending device's user) understands.
|
||||
ShortAuthenticationString []SASMethod `json:"short_authentication_string"`
|
||||
// The user that the event is sent to for in-room verification.
|
||||
To id.UserID `json:"to,omitempty"`
|
||||
// Original event ID for in-room verification.
|
||||
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
|
||||
}
|
||||
|
||||
func (vsec *VerificationStartEventContent) SupportsKeyAgreementProtocol(proto KeyAgreementProtocol) bool {
|
||||
for _, supportedProto := range vsec.KeyAgreementProtocols {
|
||||
if supportedProto == proto {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (vsec *VerificationStartEventContent) SupportsHashMethod(alg VerificationHashMethod) bool {
|
||||
for _, supportedAlg := range vsec.Hashes {
|
||||
if supportedAlg == alg {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (vsec *VerificationStartEventContent) SupportsMACMethod(meth MACMethod) bool {
|
||||
for _, supportedMeth := range vsec.MessageAuthenticationCodes {
|
||||
if supportedMeth == meth {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (vsec *VerificationStartEventContent) SupportsSASMethod(meth SASMethod) bool {
|
||||
for _, supportedMeth := range vsec.ShortAuthenticationString {
|
||||
if supportedMeth == meth {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (vsec *VerificationStartEventContent) GetRelatesTo() *RelatesTo {
|
||||
if vsec.RelatesTo == nil {
|
||||
vsec.RelatesTo = &RelatesTo{}
|
||||
}
|
||||
return vsec.RelatesTo
|
||||
}
|
||||
|
||||
func (vsec *VerificationStartEventContent) OptionalGetRelatesTo() *RelatesTo {
|
||||
return vsec.RelatesTo
|
||||
}
|
||||
|
||||
func (vsec *VerificationStartEventContent) SetRelatesTo(rel *RelatesTo) {
|
||||
vsec.RelatesTo = rel
|
||||
}
|
||||
|
||||
// VerificationReadyEventContent represents the content of a m.key.verification.ready event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mkeyverificationready
|
||||
type VerificationReadyEventContent struct {
|
||||
// The device ID which accepted the process.
|
||||
FromDevice id.DeviceID `json:"from_device"`
|
||||
// The verification methods supported by the sender.
|
||||
Methods []VerificationMethod `json:"methods"`
|
||||
// Original event ID for in-room verification.
|
||||
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
|
||||
}
|
||||
|
||||
var _ Relatable = (*VerificationReadyEventContent)(nil)
|
||||
|
||||
func (vrec *VerificationReadyEventContent) GetRelatesTo() *RelatesTo {
|
||||
if vrec.RelatesTo == nil {
|
||||
vrec.RelatesTo = &RelatesTo{}
|
||||
}
|
||||
return vrec.RelatesTo
|
||||
}
|
||||
|
||||
func (vrec *VerificationReadyEventContent) OptionalGetRelatesTo() *RelatesTo {
|
||||
return vrec.RelatesTo
|
||||
}
|
||||
|
||||
func (vrec *VerificationReadyEventContent) SetRelatesTo(rel *RelatesTo) {
|
||||
vrec.RelatesTo = rel
|
||||
}
|
||||
|
||||
// VerificationAcceptEventContent represents the content of a m.key.verification.accept to_device event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mkeyverificationaccept
|
||||
type VerificationAcceptEventContent struct {
|
||||
// An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.
|
||||
TransactionID string `json:"transaction_id,omitempty"`
|
||||
// The verification method to use.
|
||||
Method VerificationMethod `json:"method"`
|
||||
// The key agreement protocol the device is choosing to use, out of the options in the m.key.verification.start message.
|
||||
KeyAgreementProtocol KeyAgreementProtocol `json:"key_agreement_protocol"`
|
||||
// The hash method the device is choosing to use, out of the options in the m.key.verification.start message.
|
||||
Hash VerificationHashMethod `json:"hash"`
|
||||
// The message authentication code the device is choosing to use, out of the options in the m.key.verification.start message.
|
||||
MessageAuthenticationCode MACMethod `json:"message_authentication_code"`
|
||||
// The SAS methods both devices involved in the verification process understand. Must be a subset of the options in the m.key.verification.start message.
|
||||
ShortAuthenticationString []SASMethod `json:"short_authentication_string"`
|
||||
// The hash (encoded as unpadded base64) of the concatenation of the device's ephemeral public key (encoded as unpadded base64) and the canonical JSON representation of the m.key.verification.start message.
|
||||
Commitment string `json:"commitment"`
|
||||
// The user that the event is sent to for in-room verification.
|
||||
To id.UserID `json:"to,omitempty"`
|
||||
// Original event ID for in-room verification.
|
||||
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
|
||||
}
|
||||
|
||||
func (vaec *VerificationAcceptEventContent) GetRelatesTo() *RelatesTo {
|
||||
if vaec.RelatesTo == nil {
|
||||
vaec.RelatesTo = &RelatesTo{}
|
||||
}
|
||||
return vaec.RelatesTo
|
||||
}
|
||||
|
||||
func (vaec *VerificationAcceptEventContent) OptionalGetRelatesTo() *RelatesTo {
|
||||
return vaec.RelatesTo
|
||||
}
|
||||
|
||||
func (vaec *VerificationAcceptEventContent) SetRelatesTo(rel *RelatesTo) {
|
||||
vaec.RelatesTo = rel
|
||||
}
|
||||
|
||||
// VerificationKeyEventContent represents the content of a m.key.verification.key to_device event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mkeyverificationkey
|
||||
type VerificationKeyEventContent struct {
|
||||
// An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.
|
||||
TransactionID string `json:"transaction_id,omitempty"`
|
||||
// The device's ephemeral public key, encoded as unpadded base64.
|
||||
Key string `json:"key"`
|
||||
// The user that the event is sent to for in-room verification.
|
||||
To id.UserID `json:"to,omitempty"`
|
||||
// Original event ID for in-room verification.
|
||||
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
|
||||
}
|
||||
|
||||
func (vkec *VerificationKeyEventContent) GetRelatesTo() *RelatesTo {
|
||||
if vkec.RelatesTo == nil {
|
||||
vkec.RelatesTo = &RelatesTo{}
|
||||
}
|
||||
return vkec.RelatesTo
|
||||
}
|
||||
|
||||
func (vkec *VerificationKeyEventContent) OptionalGetRelatesTo() *RelatesTo {
|
||||
return vkec.RelatesTo
|
||||
}
|
||||
|
||||
func (vkec *VerificationKeyEventContent) SetRelatesTo(rel *RelatesTo) {
|
||||
vkec.RelatesTo = rel
|
||||
}
|
||||
|
||||
// VerificationMacEventContent represents the content of a m.key.verification.mac to_device event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mkeyverificationmac
|
||||
type VerificationMacEventContent struct {
|
||||
// An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.
|
||||
TransactionID string `json:"transaction_id,omitempty"`
|
||||
// A map of the key ID to the MAC of the key, using the algorithm in the verification process. The MAC is encoded as unpadded base64.
|
||||
Mac map[id.KeyID]string `json:"mac"`
|
||||
// The MAC of the comma-separated, sorted, list of key IDs given in the mac property, encoded as unpadded base64.
|
||||
Keys string `json:"keys"`
|
||||
// The user that the event is sent to for in-room verification.
|
||||
To id.UserID `json:"to,omitempty"`
|
||||
// Original event ID for in-room verification.
|
||||
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
|
||||
}
|
||||
|
||||
func (vmec *VerificationMacEventContent) GetRelatesTo() *RelatesTo {
|
||||
if vmec.RelatesTo == nil {
|
||||
vmec.RelatesTo = &RelatesTo{}
|
||||
}
|
||||
return vmec.RelatesTo
|
||||
}
|
||||
|
||||
func (vmec *VerificationMacEventContent) OptionalGetRelatesTo() *RelatesTo {
|
||||
return vmec.RelatesTo
|
||||
}
|
||||
|
||||
func (vmec *VerificationMacEventContent) SetRelatesTo(rel *RelatesTo) {
|
||||
vmec.RelatesTo = rel
|
||||
}
|
||||
|
||||
type VerificationCancelCode string
|
||||
|
||||
const (
|
||||
VerificationCancelByUser VerificationCancelCode = "m.user"
|
||||
VerificationCancelByTimeout VerificationCancelCode = "m.timeout"
|
||||
VerificationCancelUnknownTransaction VerificationCancelCode = "m.unknown_transaction"
|
||||
VerificationCancelUnknownMethod VerificationCancelCode = "m.unknown_method"
|
||||
VerificationCancelUnexpectedMessage VerificationCancelCode = "m.unexpected_message"
|
||||
VerificationCancelKeyMismatch VerificationCancelCode = "m.key_mismatch"
|
||||
VerificationCancelUserMismatch VerificationCancelCode = "m.user_mismatch"
|
||||
VerificationCancelInvalidMessage VerificationCancelCode = "m.invalid_message"
|
||||
VerificationCancelAccepted VerificationCancelCode = "m.accepted"
|
||||
VerificationCancelSASMismatch VerificationCancelCode = "m.mismatched_sas"
|
||||
VerificationCancelCommitmentMismatch VerificationCancelCode = "m.mismatched_commitment"
|
||||
)
|
||||
|
||||
// VerificationCancelEventContent represents the content of a m.key.verification.cancel to_device event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mkeyverificationcancel
|
||||
type VerificationCancelEventContent struct {
|
||||
// The opaque identifier for the verification process/request.
|
||||
TransactionID string `json:"transaction_id,omitempty"`
|
||||
// A human readable description of the code. The client should only rely on this string if it does not understand the code.
|
||||
Reason string `json:"reason"`
|
||||
// The error code for why the process/request was cancelled by the user.
|
||||
Code VerificationCancelCode `json:"code"`
|
||||
// The user that the event is sent to for in-room verification.
|
||||
To id.UserID `json:"to,omitempty"`
|
||||
// Original event ID for in-room verification.
|
||||
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
|
||||
}
|
||||
|
||||
func (vcec *VerificationCancelEventContent) GetRelatesTo() *RelatesTo {
|
||||
if vcec.RelatesTo == nil {
|
||||
vcec.RelatesTo = &RelatesTo{}
|
||||
}
|
||||
return vcec.RelatesTo
|
||||
}
|
||||
|
||||
func (vcec *VerificationCancelEventContent) OptionalGetRelatesTo() *RelatesTo {
|
||||
return vcec.RelatesTo
|
||||
}
|
||||
|
||||
func (vcec *VerificationCancelEventContent) SetRelatesTo(rel *RelatesTo) {
|
||||
vcec.RelatesTo = rel
|
||||
}
|
||||
116
vendor/maunium.net/go/mautrix/event/voip.go
generated
vendored
Normal file
116
vendor/maunium.net/go/mautrix/event/voip.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type CallHangupReason string
|
||||
|
||||
const (
|
||||
CallHangupICEFailed CallHangupReason = "ice_failed"
|
||||
CallHangupInviteTimeout CallHangupReason = "invite_timeout"
|
||||
CallHangupUserHangup CallHangupReason = "user_hangup"
|
||||
CallHangupUserMediaFailed CallHangupReason = "user_media_failed"
|
||||
CallHangupUnknownError CallHangupReason = "unknown_error"
|
||||
)
|
||||
|
||||
type CallDataType string
|
||||
|
||||
const (
|
||||
CallDataTypeOffer CallDataType = "offer"
|
||||
CallDataTypeAnswer CallDataType = "answer"
|
||||
)
|
||||
|
||||
type CallData struct {
|
||||
SDP string `json:"sdp"`
|
||||
Type CallDataType `json:"type"`
|
||||
}
|
||||
|
||||
type CallCandidate struct {
|
||||
Candidate string `json:"candidate"`
|
||||
SDPMLineIndex int `json:"sdpMLineIndex"`
|
||||
SDPMID string `json:"sdpMid"`
|
||||
}
|
||||
|
||||
type CallVersion string
|
||||
|
||||
func (cv *CallVersion) UnmarshalJSON(raw []byte) error {
|
||||
var numberVersion int
|
||||
err := json.Unmarshal(raw, &numberVersion)
|
||||
if err != nil {
|
||||
var stringVersion string
|
||||
err = json.Unmarshal(raw, &stringVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse CallVersion: %w", err)
|
||||
}
|
||||
*cv = CallVersion(stringVersion)
|
||||
} else {
|
||||
*cv = CallVersion(strconv.Itoa(numberVersion))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cv *CallVersion) MarshalJSON() ([]byte, error) {
|
||||
for _, char := range *cv {
|
||||
if char < '0' || char > '9' {
|
||||
// The version contains weird characters, return as string.
|
||||
return json.Marshal(string(*cv))
|
||||
}
|
||||
}
|
||||
// The version consists of only ASCII digits, return as an integer.
|
||||
return []byte(*cv), nil
|
||||
}
|
||||
|
||||
func (cv *CallVersion) Int() (int, error) {
|
||||
return strconv.Atoi(string(*cv))
|
||||
}
|
||||
|
||||
type BaseCallEventContent struct {
|
||||
CallID string `json:"call_id"`
|
||||
PartyID string `json:"party_id"`
|
||||
Version CallVersion `json:"version"`
|
||||
}
|
||||
|
||||
type CallInviteEventContent struct {
|
||||
BaseCallEventContent
|
||||
Lifetime int `json:"lifetime"`
|
||||
Offer CallData `json:"offer"`
|
||||
}
|
||||
|
||||
type CallCandidatesEventContent struct {
|
||||
BaseCallEventContent
|
||||
Candidates []CallCandidate `json:"candidates"`
|
||||
}
|
||||
|
||||
type CallRejectEventContent struct {
|
||||
BaseCallEventContent
|
||||
}
|
||||
|
||||
type CallAnswerEventContent struct {
|
||||
BaseCallEventContent
|
||||
Answer CallData `json:"answer"`
|
||||
}
|
||||
|
||||
type CallSelectAnswerEventContent struct {
|
||||
BaseCallEventContent
|
||||
SelectedPartyID string `json:"selected_party_id"`
|
||||
}
|
||||
|
||||
type CallNegotiateEventContent struct {
|
||||
BaseCallEventContent
|
||||
Lifetime int `json:"lifetime"`
|
||||
Description CallData `json:"description"`
|
||||
}
|
||||
|
||||
type CallHangupEventContent struct {
|
||||
BaseCallEventContent
|
||||
Reason CallHangupReason `json:"reason"`
|
||||
}
|
||||
93
vendor/maunium.net/go/mautrix/filter.go
generated
vendored
Normal file
93
vendor/maunium.net/go/mautrix/filter.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2017 Jan Christian Grünhage
|
||||
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type EventFormat string
|
||||
|
||||
const (
|
||||
EventFormatClient EventFormat = "client"
|
||||
EventFormatFederation EventFormat = "federation"
|
||||
)
|
||||
|
||||
// Filter is used by clients to specify how the server should filter responses to e.g. sync requests
|
||||
// Specified by: https://spec.matrix.org/v1.2/client-server-api/#filtering
|
||||
type Filter struct {
|
||||
AccountData FilterPart `json:"account_data,omitempty"`
|
||||
EventFields []string `json:"event_fields,omitempty"`
|
||||
EventFormat EventFormat `json:"event_format,omitempty"`
|
||||
Presence FilterPart `json:"presence,omitempty"`
|
||||
Room RoomFilter `json:"room,omitempty"`
|
||||
}
|
||||
|
||||
// RoomFilter is used to define filtering rules for room events
|
||||
type RoomFilter struct {
|
||||
AccountData FilterPart `json:"account_data,omitempty"`
|
||||
Ephemeral FilterPart `json:"ephemeral,omitempty"`
|
||||
IncludeLeave bool `json:"include_leave,omitempty"`
|
||||
NotRooms []id.RoomID `json:"not_rooms,omitempty"`
|
||||
Rooms []id.RoomID `json:"rooms,omitempty"`
|
||||
State FilterPart `json:"state,omitempty"`
|
||||
Timeline FilterPart `json:"timeline,omitempty"`
|
||||
}
|
||||
|
||||
// FilterPart is used to define filtering rules for specific categories of events
|
||||
type FilterPart struct {
|
||||
NotRooms []id.RoomID `json:"not_rooms,omitempty"`
|
||||
Rooms []id.RoomID `json:"rooms,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
NotSenders []id.UserID `json:"not_senders,omitempty"`
|
||||
NotTypes []event.Type `json:"not_types,omitempty"`
|
||||
Senders []id.UserID `json:"senders,omitempty"`
|
||||
Types []event.Type `json:"types,omitempty"`
|
||||
ContainsURL *bool `json:"contains_url,omitempty"`
|
||||
|
||||
LazyLoadMembers bool `json:"lazy_load_members,omitempty"`
|
||||
IncludeRedundantMembers bool `json:"include_redundant_members,omitempty"`
|
||||
}
|
||||
|
||||
// Validate checks if the filter contains valid property values
|
||||
func (filter *Filter) Validate() error {
|
||||
if filter.EventFormat != EventFormatClient && filter.EventFormat != EventFormatFederation {
|
||||
return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in the request
|
||||
func DefaultFilter() Filter {
|
||||
return Filter{
|
||||
AccountData: DefaultFilterPart(),
|
||||
EventFields: nil,
|
||||
EventFormat: "client",
|
||||
Presence: DefaultFilterPart(),
|
||||
Room: RoomFilter{
|
||||
AccountData: DefaultFilterPart(),
|
||||
Ephemeral: DefaultFilterPart(),
|
||||
IncludeLeave: false,
|
||||
NotRooms: nil,
|
||||
Rooms: nil,
|
||||
State: DefaultFilterPart(),
|
||||
Timeline: DefaultFilterPart(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultFilterPart returns the default filter part used by the Matrix server if no filter is provided in the request
|
||||
func DefaultFilterPart() FilterPart {
|
||||
return FilterPart{
|
||||
NotRooms: nil,
|
||||
Rooms: nil,
|
||||
Limit: 20,
|
||||
NotSenders: nil,
|
||||
NotTypes: nil,
|
||||
Senders: nil,
|
||||
Types: nil,
|
||||
}
|
||||
}
|
||||
158
vendor/maunium.net/go/mautrix/id/contenturi.go
generated
vendored
Normal file
158
vendor/maunium.net/go/mautrix/id/contenturi.go
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package id
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
InvalidContentURI = errors.New("invalid Matrix content URI")
|
||||
InputNotJSONString = errors.New("input doesn't look like a JSON string")
|
||||
)
|
||||
|
||||
// ContentURIString is a string that's expected to be a Matrix content URI.
|
||||
// It's useful for delaying the parsing of the content URI to move errors from the event content
|
||||
// JSON parsing step to a later step where more appropriate errors can be produced.
|
||||
type ContentURIString string
|
||||
|
||||
func (uriString ContentURIString) Parse() (ContentURI, error) {
|
||||
return ParseContentURI(string(uriString))
|
||||
}
|
||||
|
||||
func (uriString ContentURIString) ParseOrIgnore() ContentURI {
|
||||
parsed, _ := ParseContentURI(string(uriString))
|
||||
return parsed
|
||||
}
|
||||
|
||||
// ContentURI represents a Matrix content URI.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#matrix-content-mxc-uris
|
||||
type ContentURI struct {
|
||||
Homeserver string
|
||||
FileID string
|
||||
}
|
||||
|
||||
func MustParseContentURI(uri string) ContentURI {
|
||||
parsed, err := ParseContentURI(uri)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
// ParseContentURI parses a Matrix content URI.
|
||||
func ParseContentURI(uri string) (parsed ContentURI, err error) {
|
||||
if len(uri) == 0 {
|
||||
return
|
||||
} else if !strings.HasPrefix(uri, "mxc://") {
|
||||
err = InvalidContentURI
|
||||
} else if index := strings.IndexRune(uri[6:], '/'); index == -1 || index == len(uri)-7 {
|
||||
err = InvalidContentURI
|
||||
} else {
|
||||
parsed.Homeserver = uri[6 : 6+index]
|
||||
parsed.FileID = uri[6+index+1:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var mxcBytes = []byte("mxc://")
|
||||
|
||||
func ParseContentURIBytes(uri []byte) (parsed ContentURI, err error) {
|
||||
if len(uri) == 0 {
|
||||
return
|
||||
} else if !bytes.HasPrefix(uri, mxcBytes) {
|
||||
err = InvalidContentURI
|
||||
} else if index := bytes.IndexRune(uri[6:], '/'); index == -1 || index == len(uri)-7 {
|
||||
err = InvalidContentURI
|
||||
} else {
|
||||
parsed.Homeserver = string(uri[6 : 6+index])
|
||||
parsed.FileID = string(uri[6+index+1:])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (uri *ContentURI) UnmarshalJSON(raw []byte) (err error) {
|
||||
if string(raw) == "null" {
|
||||
*uri = ContentURI{}
|
||||
return nil
|
||||
} else if len(raw) < 2 || raw[0] != '"' || raw[len(raw)-1] != '"' {
|
||||
return InputNotJSONString
|
||||
}
|
||||
parsed, err := ParseContentURIBytes(raw[1 : len(raw)-1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*uri = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uri *ContentURI) MarshalJSON() ([]byte, error) {
|
||||
if uri == nil || uri.IsEmpty() {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return json.Marshal(uri.String())
|
||||
}
|
||||
|
||||
func (uri *ContentURI) UnmarshalText(raw []byte) (err error) {
|
||||
parsed, err := ParseContentURIBytes(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*uri = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uri ContentURI) MarshalText() ([]byte, error) {
|
||||
return []byte(uri.String()), nil
|
||||
}
|
||||
|
||||
func (uri *ContentURI) Scan(i interface{}) error {
|
||||
var parsed ContentURI
|
||||
var err error
|
||||
switch value := i.(type) {
|
||||
case nil:
|
||||
// don't do anything, set uri to empty
|
||||
case []byte:
|
||||
parsed, err = ParseContentURIBytes(value)
|
||||
case string:
|
||||
parsed, err = ParseContentURI(value)
|
||||
default:
|
||||
return fmt.Errorf("invalid type %T for ContentURI.Scan", i)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*uri = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uri *ContentURI) Value() (driver.Value, error) {
|
||||
if uri == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return uri.String(), nil
|
||||
}
|
||||
|
||||
func (uri ContentURI) String() string {
|
||||
if uri.IsEmpty() {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("mxc://%s/%s", uri.Homeserver, uri.FileID)
|
||||
}
|
||||
|
||||
func (uri ContentURI) CUString() ContentURIString {
|
||||
return ContentURIString(uri.String())
|
||||
}
|
||||
|
||||
func (uri ContentURI) IsEmpty() bool {
|
||||
return len(uri.Homeserver) == 0 || len(uri.FileID) == 0
|
||||
}
|
||||
149
vendor/maunium.net/go/mautrix/id/crypto.go
generated
vendored
Normal file
149
vendor/maunium.net/go/mautrix/id/crypto.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package id
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// OlmMsgType is an Olm message type
|
||||
type OlmMsgType int
|
||||
|
||||
const (
|
||||
OlmMsgTypePreKey OlmMsgType = 0
|
||||
OlmMsgTypeMsg OlmMsgType = 1
|
||||
)
|
||||
|
||||
// Algorithm is a Matrix message encryption algorithm.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#messaging-algorithm-names
|
||||
type Algorithm string
|
||||
|
||||
const (
|
||||
AlgorithmOlmV1 Algorithm = "m.olm.v1.curve25519-aes-sha2"
|
||||
AlgorithmMegolmV1 Algorithm = "m.megolm.v1.aes-sha2"
|
||||
)
|
||||
|
||||
type KeyAlgorithm string
|
||||
|
||||
const (
|
||||
KeyAlgorithmCurve25519 KeyAlgorithm = "curve25519"
|
||||
KeyAlgorithmEd25519 KeyAlgorithm = "ed25519"
|
||||
KeyAlgorithmSignedCurve25519 KeyAlgorithm = "signed_curve25519"
|
||||
)
|
||||
|
||||
type CrossSigningUsage string
|
||||
|
||||
const (
|
||||
XSUsageMaster CrossSigningUsage = "master"
|
||||
XSUsageSelfSigning CrossSigningUsage = "self_signing"
|
||||
XSUsageUserSigning CrossSigningUsage = "user_signing"
|
||||
)
|
||||
|
||||
// A SessionID is an arbitrary string that identifies an Olm or Megolm session.
|
||||
type SessionID string
|
||||
|
||||
func (sessionID SessionID) String() string {
|
||||
return string(sessionID)
|
||||
}
|
||||
|
||||
// Ed25519 is the base64 representation of an Ed25519 public key
|
||||
type Ed25519 string
|
||||
type SigningKey = Ed25519
|
||||
|
||||
func (ed25519 Ed25519) String() string {
|
||||
return string(ed25519)
|
||||
}
|
||||
|
||||
func (ed25519 Ed25519) Fingerprint() string {
|
||||
spacedSigningKey := make([]byte, len(ed25519)+(len(ed25519)-1)/4)
|
||||
var ptr = 0
|
||||
for i, chr := range ed25519 {
|
||||
spacedSigningKey[ptr] = byte(chr)
|
||||
ptr++
|
||||
if i%4 == 3 {
|
||||
spacedSigningKey[ptr] = ' '
|
||||
ptr++
|
||||
}
|
||||
}
|
||||
return string(spacedSigningKey)
|
||||
}
|
||||
|
||||
// Curve25519 is the base64 representation of an Curve25519 public key
|
||||
type Curve25519 string
|
||||
type SenderKey = Curve25519
|
||||
type IdentityKey = Curve25519
|
||||
|
||||
func (curve25519 Curve25519) String() string {
|
||||
return string(curve25519)
|
||||
}
|
||||
|
||||
// A DeviceID is an arbitrary string that references a specific device.
|
||||
type DeviceID string
|
||||
|
||||
func (deviceID DeviceID) String() string {
|
||||
return string(deviceID)
|
||||
}
|
||||
|
||||
// A DeviceKeyID is a string formatted as <algorithm>:<device_id> that is used as the key in deviceid-key mappings.
|
||||
type DeviceKeyID string
|
||||
|
||||
func NewDeviceKeyID(algorithm KeyAlgorithm, deviceID DeviceID) DeviceKeyID {
|
||||
return DeviceKeyID(fmt.Sprintf("%s:%s", algorithm, deviceID))
|
||||
}
|
||||
|
||||
func (deviceKeyID DeviceKeyID) String() string {
|
||||
return string(deviceKeyID)
|
||||
}
|
||||
|
||||
func (deviceKeyID DeviceKeyID) Parse() (Algorithm, DeviceID) {
|
||||
index := strings.IndexRune(string(deviceKeyID), ':')
|
||||
if index < 0 || len(deviceKeyID) <= index+1 {
|
||||
return "", ""
|
||||
}
|
||||
return Algorithm(deviceKeyID[:index]), DeviceID(deviceKeyID[index+1:])
|
||||
}
|
||||
|
||||
// A KeyID a string formatted as <keyalgorithm>:<key_id> that is used as the key in one-time-key mappings.
|
||||
type KeyID string
|
||||
|
||||
func NewKeyID(algorithm KeyAlgorithm, keyID string) KeyID {
|
||||
return KeyID(fmt.Sprintf("%s:%s", algorithm, keyID))
|
||||
}
|
||||
|
||||
func (keyID KeyID) String() string {
|
||||
return string(keyID)
|
||||
}
|
||||
|
||||
func (keyID KeyID) Parse() (KeyAlgorithm, string) {
|
||||
index := strings.IndexRune(string(keyID), ':')
|
||||
if index < 0 || len(keyID) <= index+1 {
|
||||
return "", ""
|
||||
}
|
||||
return KeyAlgorithm(keyID[:index]), string(keyID[index+1:])
|
||||
}
|
||||
|
||||
// Device contains the identity details of a device and some additional info.
|
||||
type Device struct {
|
||||
UserID UserID
|
||||
DeviceID DeviceID
|
||||
IdentityKey Curve25519
|
||||
SigningKey Ed25519
|
||||
|
||||
Trust TrustState
|
||||
Deleted bool
|
||||
Name string
|
||||
}
|
||||
|
||||
func (device *Device) Fingerprint() string {
|
||||
return device.SigningKey.Fingerprint()
|
||||
}
|
||||
|
||||
type CrossSigningKey struct {
|
||||
Key Ed25519
|
||||
First Ed25519
|
||||
}
|
||||
293
vendor/maunium.net/go/mautrix/id/matrixuri.go
generated
vendored
Normal file
293
vendor/maunium.net/go/mautrix/id/matrixuri.go
generated
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package id
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Errors that can happen when parsing matrix: URIs
|
||||
var (
|
||||
ErrInvalidScheme = errors.New("matrix URI scheme must be exactly 'matrix'")
|
||||
ErrInvalidPartCount = errors.New("matrix URIs must have exactly 2 or 4 segments")
|
||||
ErrInvalidFirstSegment = errors.New("invalid identifier in first segment of matrix URI")
|
||||
ErrEmptySecondSegment = errors.New("the second segment of the matrix URI must not be empty")
|
||||
ErrInvalidThirdSegment = errors.New("invalid identifier in third segment of matrix URI")
|
||||
ErrEmptyFourthSegment = errors.New("the fourth segment of the matrix URI must not be empty when the third segment is present")
|
||||
)
|
||||
|
||||
// Errors that can happen when parsing matrix.to URLs
|
||||
var (
|
||||
ErrNotMatrixTo = errors.New("that URL is not a matrix.to URL")
|
||||
ErrInvalidMatrixToPartCount = errors.New("matrix.to URLs must have exactly 1 or 2 segments")
|
||||
ErrEmptyMatrixToPrimaryIdentifier = errors.New("the primary identifier in the matrix.to URL is empty")
|
||||
ErrInvalidMatrixToPrimaryIdentifier = errors.New("the primary identifier in the matrix.to URL has an invalid sigil")
|
||||
ErrInvalidMatrixToSecondaryIdentifier = errors.New("the secondary identifier in the matrix.to URL has an invalid sigil")
|
||||
)
|
||||
|
||||
var ErrNotMatrixToOrMatrixURI = errors.New("that URL is not a matrix.to URL nor matrix: URI")
|
||||
|
||||
// MatrixURI contains the result of parsing a matrix: URI using ParseMatrixURI
|
||||
type MatrixURI struct {
|
||||
Sigil1 rune
|
||||
Sigil2 rune
|
||||
MXID1 string
|
||||
MXID2 string
|
||||
Via []string
|
||||
Action string
|
||||
}
|
||||
|
||||
// SigilToPathSegment contains a mapping from Matrix identifier sigils to matrix: URI path segments.
|
||||
var SigilToPathSegment = map[rune]string{
|
||||
'$': "e",
|
||||
'#': "r",
|
||||
'!': "roomid",
|
||||
'@': "u",
|
||||
}
|
||||
|
||||
func (uri *MatrixURI) getQuery() url.Values {
|
||||
q := make(url.Values)
|
||||
if uri.Via != nil && len(uri.Via) > 0 {
|
||||
q["via"] = uri.Via
|
||||
}
|
||||
if len(uri.Action) > 0 {
|
||||
q.Set("action", uri.Action)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// String converts the parsed matrix: URI back into the string representation.
|
||||
func (uri *MatrixURI) String() string {
|
||||
parts := []string{
|
||||
SigilToPathSegment[uri.Sigil1],
|
||||
url.PathEscape(uri.MXID1),
|
||||
}
|
||||
if uri.Sigil2 != 0 {
|
||||
parts = append(parts, SigilToPathSegment[uri.Sigil2], url.PathEscape(uri.MXID2))
|
||||
}
|
||||
return (&url.URL{
|
||||
Scheme: "matrix",
|
||||
Opaque: strings.Join(parts, "/"),
|
||||
RawQuery: uri.getQuery().Encode(),
|
||||
}).String()
|
||||
}
|
||||
|
||||
// MatrixToURL converts to parsed matrix: URI into a matrix.to URL
|
||||
func (uri *MatrixURI) MatrixToURL() string {
|
||||
fragment := fmt.Sprintf("#/%s", url.PathEscape(uri.PrimaryIdentifier()))
|
||||
if uri.Sigil2 != 0 {
|
||||
fragment = fmt.Sprintf("%s/%s", fragment, url.PathEscape(uri.SecondaryIdentifier()))
|
||||
}
|
||||
query := uri.getQuery().Encode()
|
||||
if len(query) > 0 {
|
||||
fragment = fmt.Sprintf("%s?%s", fragment, query)
|
||||
}
|
||||
// It would be nice to use URL{...}.String() here, but figuring out the Fragment vs RawFragment stuff is a pain
|
||||
return fmt.Sprintf("https://matrix.to/%s", fragment)
|
||||
}
|
||||
|
||||
// PrimaryIdentifier returns the first Matrix identifier in the URI.
|
||||
// Currently room IDs, room aliases and user IDs can be in the primary identifier slot.
|
||||
func (uri *MatrixURI) PrimaryIdentifier() string {
|
||||
return fmt.Sprintf("%c%s", uri.Sigil1, uri.MXID1)
|
||||
}
|
||||
|
||||
// SecondaryIdentifier returns the second Matrix identifier in the URI.
|
||||
// Currently only event IDs can be in the secondary identifier slot.
|
||||
func (uri *MatrixURI) SecondaryIdentifier() string {
|
||||
if uri.Sigil2 == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%c%s", uri.Sigil2, uri.MXID2)
|
||||
}
|
||||
|
||||
// UserID returns the user ID from the URI if the primary identifier is a user ID.
|
||||
func (uri *MatrixURI) UserID() UserID {
|
||||
if uri.Sigil1 == '@' {
|
||||
return UserID(uri.PrimaryIdentifier())
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// RoomID returns the room ID from the URI if the primary identifier is a room ID.
|
||||
func (uri *MatrixURI) RoomID() RoomID {
|
||||
if uri.Sigil1 == '!' {
|
||||
return RoomID(uri.PrimaryIdentifier())
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// RoomAlias returns the room alias from the URI if the primary identifier is a room alias.
|
||||
func (uri *MatrixURI) RoomAlias() RoomAlias {
|
||||
if uri.Sigil1 == '#' {
|
||||
return RoomAlias(uri.PrimaryIdentifier())
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// EventID returns the event ID from the URI if the primary identifier is a room ID or alias and the secondary identifier is an event ID.
|
||||
func (uri *MatrixURI) EventID() EventID {
|
||||
if (uri.Sigil1 == '!' || uri.Sigil1 == '#') && uri.Sigil2 == '$' {
|
||||
return EventID(uri.SecondaryIdentifier())
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ParseMatrixURIOrMatrixToURL parses the given matrix.to URL or matrix: URI into a unified representation.
|
||||
func ParseMatrixURIOrMatrixToURL(uri string) (*MatrixURI, error) {
|
||||
parsed, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse URI: %w", err)
|
||||
}
|
||||
if parsed.Scheme == "matrix" {
|
||||
return ProcessMatrixURI(parsed)
|
||||
} else if strings.HasSuffix(parsed.Hostname(), "matrix.to") {
|
||||
return ProcessMatrixToURL(parsed)
|
||||
} else {
|
||||
return nil, ErrNotMatrixToOrMatrixURI
|
||||
}
|
||||
}
|
||||
|
||||
// ParseMatrixURI implements the matrix: URI parsing algorithm.
|
||||
//
|
||||
// Currently specified in https://github.com/matrix-org/matrix-doc/blob/master/proposals/2312-matrix-uri.md#uri-parsing-algorithm
|
||||
func ParseMatrixURI(uri string) (*MatrixURI, error) {
|
||||
// Step 1: parse the URI according to RFC 3986
|
||||
parsed, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse URI: %w", err)
|
||||
}
|
||||
return ProcessMatrixURI(parsed)
|
||||
}
|
||||
|
||||
// ProcessMatrixURI implements steps 2-7 of the matrix: URI parsing algorithm
|
||||
// (i.e. everything except parsing the URI itself, which is done with url.Parse or ParseMatrixURI)
|
||||
func ProcessMatrixURI(uri *url.URL) (*MatrixURI, error) {
|
||||
// Step 2: check that scheme is exactly `matrix`
|
||||
if uri.Scheme != "matrix" {
|
||||
return nil, ErrInvalidScheme
|
||||
}
|
||||
|
||||
// Step 3: split the path into segments separated by /
|
||||
parts := strings.Split(uri.Opaque, "/")
|
||||
|
||||
// Step 4: Check that the URI contains either 2 or 4 segments
|
||||
if len(parts) != 2 && len(parts) != 4 {
|
||||
return nil, ErrInvalidPartCount
|
||||
}
|
||||
|
||||
var parsed MatrixURI
|
||||
|
||||
// Step 5: Construct the top-level Matrix identifier
|
||||
// a: find the sigil from the first segment
|
||||
switch parts[0] {
|
||||
case "u", "user":
|
||||
parsed.Sigil1 = '@'
|
||||
case "r", "room":
|
||||
parsed.Sigil1 = '#'
|
||||
case "roomid":
|
||||
parsed.Sigil1 = '!'
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: '%s'", ErrInvalidFirstSegment, parts[0])
|
||||
}
|
||||
// b: find the identifier from the second segment
|
||||
if len(parts[1]) == 0 {
|
||||
return nil, ErrEmptySecondSegment
|
||||
}
|
||||
parsed.MXID1 = parts[1]
|
||||
|
||||
// Step 6: if the first part is a room and the URI has 4 segments, construct a second level identifier
|
||||
if (parsed.Sigil1 == '!' || parsed.Sigil1 == '#') && len(parts) == 4 {
|
||||
// a: find the sigil from the third segment
|
||||
switch parts[2] {
|
||||
case "e", "event":
|
||||
parsed.Sigil2 = '$'
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: '%s'", ErrInvalidThirdSegment, parts[0])
|
||||
}
|
||||
|
||||
// b: find the identifier from the fourth segment
|
||||
if len(parts[3]) == 0 {
|
||||
return nil, ErrEmptyFourthSegment
|
||||
}
|
||||
parsed.MXID2 = parts[3]
|
||||
}
|
||||
|
||||
// Step 7: parse the query and extract via and action items
|
||||
via, ok := uri.Query()["via"]
|
||||
if ok && len(via) > 0 {
|
||||
parsed.Via = via
|
||||
}
|
||||
action, ok := uri.Query()["action"]
|
||||
if ok && len(action) > 0 {
|
||||
parsed.Action = action[len(action)-1]
|
||||
}
|
||||
|
||||
return &parsed, nil
|
||||
}
|
||||
|
||||
// ParseMatrixToURL parses a matrix.to URL into the same container as ParseMatrixURI parses matrix: URIs.
|
||||
func ParseMatrixToURL(uri string) (*MatrixURI, error) {
|
||||
parsed, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse URL: %w", err)
|
||||
}
|
||||
return ProcessMatrixToURL(parsed)
|
||||
}
|
||||
|
||||
// ProcessMatrixToURL is the equivalent of ProcessMatrixURI for matrix.to URLs.
|
||||
func ProcessMatrixToURL(uri *url.URL) (*MatrixURI, error) {
|
||||
if !strings.HasSuffix(uri.Hostname(), "matrix.to") {
|
||||
return nil, ErrNotMatrixTo
|
||||
}
|
||||
|
||||
initialSplit := strings.SplitN(uri.Fragment, "?", 2)
|
||||
parts := strings.Split(initialSplit[0], "/")
|
||||
if len(initialSplit) > 1 {
|
||||
uri.RawQuery = initialSplit[1]
|
||||
}
|
||||
|
||||
if len(parts) < 2 || len(parts) > 3 {
|
||||
return nil, ErrInvalidMatrixToPartCount
|
||||
}
|
||||
|
||||
if len(parts[1]) == 0 {
|
||||
return nil, ErrEmptyMatrixToPrimaryIdentifier
|
||||
}
|
||||
|
||||
var parsed MatrixURI
|
||||
|
||||
parsed.Sigil1 = rune(parts[1][0])
|
||||
parsed.MXID1 = parts[1][1:]
|
||||
_, isKnown := SigilToPathSegment[parsed.Sigil1]
|
||||
if !isKnown {
|
||||
return nil, ErrInvalidMatrixToPrimaryIdentifier
|
||||
}
|
||||
|
||||
if len(parts) == 3 && len(parts[2]) > 0 {
|
||||
parsed.Sigil2 = rune(parts[2][0])
|
||||
parsed.MXID2 = parts[2][1:]
|
||||
_, isKnown = SigilToPathSegment[parsed.Sigil2]
|
||||
if !isKnown {
|
||||
return nil, ErrInvalidMatrixToSecondaryIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
via, ok := uri.Query()["via"]
|
||||
if ok && len(via) > 0 {
|
||||
parsed.Via = via
|
||||
}
|
||||
action, ok := uri.Query()["action"]
|
||||
if ok && len(action) > 0 {
|
||||
parsed.Action = action[len(action)-1]
|
||||
}
|
||||
|
||||
return &parsed, nil
|
||||
}
|
||||
83
vendor/maunium.net/go/mautrix/id/opaque.go
generated
vendored
Normal file
83
vendor/maunium.net/go/mautrix/id/opaque.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package id
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A RoomID is a string starting with ! that references a specific room.
|
||||
// https://matrix.org/docs/spec/appendices#room-ids-and-event-ids
|
||||
type RoomID string
|
||||
|
||||
// A RoomAlias is a string starting with # that can be resolved into.
|
||||
// https://matrix.org/docs/spec/appendices#room-aliases
|
||||
type RoomAlias string
|
||||
|
||||
func NewRoomAlias(localpart, server string) RoomAlias {
|
||||
return RoomAlias(fmt.Sprintf("#%s:%s", localpart, server))
|
||||
}
|
||||
|
||||
// An EventID is a string starting with $ that references a specific event.
|
||||
//
|
||||
// https://matrix.org/docs/spec/appendices#room-ids-and-event-ids
|
||||
// https://matrix.org/docs/spec/rooms/v4#event-ids
|
||||
type EventID string
|
||||
|
||||
// A BatchID is a string identifying a batch of events being backfilled to a room.
|
||||
// https://github.com/matrix-org/matrix-doc/pull/2716
|
||||
type BatchID string
|
||||
|
||||
func (roomID RoomID) String() string {
|
||||
return string(roomID)
|
||||
}
|
||||
|
||||
func (roomID RoomID) URI(via ...string) *MatrixURI {
|
||||
return &MatrixURI{
|
||||
Sigil1: '!',
|
||||
MXID1: string(roomID)[1:],
|
||||
Via: via,
|
||||
}
|
||||
}
|
||||
|
||||
func (roomID RoomID) EventURI(eventID EventID, via ...string) *MatrixURI {
|
||||
return &MatrixURI{
|
||||
Sigil1: '!',
|
||||
MXID1: string(roomID)[1:],
|
||||
Sigil2: '$',
|
||||
MXID2: string(eventID)[1:],
|
||||
Via: via,
|
||||
}
|
||||
}
|
||||
|
||||
func (roomAlias RoomAlias) String() string {
|
||||
return string(roomAlias)
|
||||
}
|
||||
|
||||
func (roomAlias RoomAlias) URI() *MatrixURI {
|
||||
return &MatrixURI{
|
||||
Sigil1: '#',
|
||||
MXID1: string(roomAlias)[1:],
|
||||
}
|
||||
}
|
||||
|
||||
func (roomAlias RoomAlias) EventURI(eventID EventID) *MatrixURI {
|
||||
return &MatrixURI{
|
||||
Sigil1: '#',
|
||||
MXID1: string(roomAlias)[1:],
|
||||
Sigil2: '$',
|
||||
MXID2: string(eventID)[1:],
|
||||
}
|
||||
}
|
||||
|
||||
func (eventID EventID) String() string {
|
||||
return string(eventID)
|
||||
}
|
||||
|
||||
func (batchID BatchID) String() string {
|
||||
return string(batchID)
|
||||
}
|
||||
87
vendor/maunium.net/go/mautrix/id/trust.go
generated
vendored
Normal file
87
vendor/maunium.net/go/mautrix/id/trust.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package id
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TrustState determines how trusted a device is.
|
||||
type TrustState int
|
||||
|
||||
const (
|
||||
TrustStateBlacklisted TrustState = -100
|
||||
TrustStateUnset TrustState = 0
|
||||
TrustStateUnknownDevice TrustState = 10
|
||||
TrustStateForwarded TrustState = 20
|
||||
TrustStateCrossSignedUntrusted TrustState = 50
|
||||
TrustStateCrossSignedTOFU TrustState = 100
|
||||
TrustStateCrossSignedVerified TrustState = 200
|
||||
TrustStateVerified TrustState = 300
|
||||
TrustStateInvalid TrustState = (1 << 31) - 1
|
||||
)
|
||||
|
||||
func (ts *TrustState) UnmarshalText(data []byte) error {
|
||||
strData := string(data)
|
||||
state := ParseTrustState(strData)
|
||||
if state == TrustStateInvalid {
|
||||
return fmt.Errorf("invalid trust state %q", strData)
|
||||
}
|
||||
*ts = state
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ts *TrustState) MarshalText() ([]byte, error) {
|
||||
return []byte(ts.String()), nil
|
||||
}
|
||||
|
||||
func ParseTrustState(val string) TrustState {
|
||||
switch strings.ToLower(val) {
|
||||
case "blacklisted":
|
||||
return TrustStateBlacklisted
|
||||
case "unverified":
|
||||
return TrustStateUnset
|
||||
case "cross-signed-untrusted":
|
||||
return TrustStateCrossSignedUntrusted
|
||||
case "unknown-device":
|
||||
return TrustStateUnknownDevice
|
||||
case "forwarded":
|
||||
return TrustStateForwarded
|
||||
case "cross-signed-tofu", "cross-signed":
|
||||
return TrustStateCrossSignedTOFU
|
||||
case "cross-signed-verified", "cross-signed-trusted":
|
||||
return TrustStateCrossSignedVerified
|
||||
case "verified":
|
||||
return TrustStateVerified
|
||||
default:
|
||||
return TrustStateInvalid
|
||||
}
|
||||
}
|
||||
|
||||
func (ts TrustState) String() string {
|
||||
switch ts {
|
||||
case TrustStateBlacklisted:
|
||||
return "blacklisted"
|
||||
case TrustStateUnset:
|
||||
return "unverified"
|
||||
case TrustStateCrossSignedUntrusted:
|
||||
return "cross-signed-untrusted"
|
||||
case TrustStateUnknownDevice:
|
||||
return "unknown-device"
|
||||
case TrustStateForwarded:
|
||||
return "forwarded"
|
||||
case TrustStateCrossSignedTOFU:
|
||||
return "cross-signed-tofu"
|
||||
case TrustStateCrossSignedVerified:
|
||||
return "cross-signed-verified"
|
||||
case TrustStateVerified:
|
||||
return "verified"
|
||||
default:
|
||||
return "invalid"
|
||||
}
|
||||
}
|
||||
224
vendor/maunium.net/go/mautrix/id/userid.go
generated
vendored
Normal file
224
vendor/maunium.net/go/mautrix/id/userid.go
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package id
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UserID represents a Matrix user ID.
|
||||
// https://matrix.org/docs/spec/appendices#user-identifiers
|
||||
type UserID string
|
||||
|
||||
const UserIDMaxLength = 255
|
||||
|
||||
func NewUserID(localpart, homeserver string) UserID {
|
||||
return UserID(fmt.Sprintf("@%s:%s", localpart, homeserver))
|
||||
}
|
||||
|
||||
func NewEncodedUserID(localpart, homeserver string) UserID {
|
||||
return NewUserID(EncodeUserLocalpart(localpart), homeserver)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidUserID = errors.New("is not a valid user ID")
|
||||
ErrNoncompliantLocalpart = errors.New("contains characters that are not allowed")
|
||||
ErrUserIDTooLong = errors.New("the given user ID is longer than 255 characters")
|
||||
ErrEmptyLocalpart = errors.New("empty localparts are not allowed")
|
||||
)
|
||||
|
||||
// Parse parses the user ID into the localpart and server name.
|
||||
//
|
||||
// Note that this only enforces very basic user ID formatting requirements: user IDs start with
|
||||
// a @, and contain a : after the @. If you want to enforce localpart validity, see the
|
||||
// ParseAndValidate and ValidateUserLocalpart functions.
|
||||
func (userID UserID) Parse() (localpart, homeserver string, err error) {
|
||||
if len(userID) == 0 || userID[0] != '@' || !strings.ContainsRune(string(userID), ':') {
|
||||
// This error wrapping lets you use errors.Is() nicely even though the message contains the user ID
|
||||
err = fmt.Errorf("'%s' %w", userID, ErrInvalidUserID)
|
||||
return
|
||||
}
|
||||
parts := strings.SplitN(string(userID), ":", 2)
|
||||
localpart, homeserver = strings.TrimPrefix(parts[0], "@"), parts[1]
|
||||
return
|
||||
}
|
||||
|
||||
func (userID UserID) Localpart() string {
|
||||
localpart, _, _ := userID.Parse()
|
||||
return localpart
|
||||
}
|
||||
|
||||
func (userID UserID) Homeserver() string {
|
||||
_, homeserver, _ := userID.Parse()
|
||||
return homeserver
|
||||
}
|
||||
|
||||
// URI returns the user ID as a MatrixURI struct, which can then be stringified into a matrix: URI or a matrix.to URL.
|
||||
//
|
||||
// This does not parse or validate the user ID. Use the ParseAndValidate method if you want to ensure the user ID is valid first.
|
||||
func (userID UserID) URI() *MatrixURI {
|
||||
return &MatrixURI{
|
||||
Sigil1: '@',
|
||||
MXID1: string(userID)[1:],
|
||||
}
|
||||
}
|
||||
|
||||
var ValidLocalpartRegex = regexp.MustCompile("^[0-9a-z-.=_/]+$")
|
||||
|
||||
// ValidateUserLocalpart validates a Matrix user ID localpart using the grammar
|
||||
// in https://matrix.org/docs/spec/appendices#user-identifier
|
||||
func ValidateUserLocalpart(localpart string) error {
|
||||
if len(localpart) == 0 {
|
||||
return ErrEmptyLocalpart
|
||||
} else if !ValidLocalpartRegex.MatchString(localpart) {
|
||||
return fmt.Errorf("'%s' %w", localpart, ErrNoncompliantLocalpart)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseAndValidate parses the user ID into the localpart and server name like Parse,
|
||||
// and also validates that the localpart is allowed according to the user identifiers spec.
|
||||
func (userID UserID) ParseAndValidate() (localpart, homeserver string, err error) {
|
||||
localpart, homeserver, err = userID.Parse()
|
||||
if err == nil {
|
||||
err = ValidateUserLocalpart(localpart)
|
||||
}
|
||||
if err == nil && len(userID) > UserIDMaxLength {
|
||||
err = ErrUserIDTooLong
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (userID UserID) ParseAndDecode() (localpart, homeserver string, err error) {
|
||||
localpart, homeserver, err = userID.ParseAndValidate()
|
||||
if err == nil {
|
||||
localpart, err = DecodeUserLocalpart(localpart)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (userID UserID) String() string {
|
||||
return string(userID)
|
||||
}
|
||||
|
||||
const lowerhex = "0123456789abcdef"
|
||||
|
||||
// encode the given byte using quoted-printable encoding (e.g "=2f")
|
||||
// and writes it to the buffer
|
||||
// See https://golang.org/src/mime/quotedprintable/writer.go
|
||||
func encode(buf *bytes.Buffer, b byte) {
|
||||
buf.WriteByte('=')
|
||||
buf.WriteByte(lowerhex[b>>4])
|
||||
buf.WriteByte(lowerhex[b&0x0f])
|
||||
}
|
||||
|
||||
// escape the given alpha character and writes it to the buffer
|
||||
func escape(buf *bytes.Buffer, b byte) {
|
||||
buf.WriteByte('_')
|
||||
if b == '_' {
|
||||
buf.WriteByte('_') // another _
|
||||
} else {
|
||||
buf.WriteByte(b + 0x20) // ASCII shift A-Z to a-z
|
||||
}
|
||||
}
|
||||
|
||||
func shouldEncode(b byte) bool {
|
||||
return b != '-' && b != '.' && b != '_' && !(b >= '0' && b <= '9') && !(b >= 'a' && b <= 'z') && !(b >= 'A' && b <= 'Z')
|
||||
}
|
||||
|
||||
func shouldEscape(b byte) bool {
|
||||
return (b >= 'A' && b <= 'Z') || b == '_'
|
||||
}
|
||||
|
||||
func isValidByte(b byte) bool {
|
||||
return isValidEscapedChar(b) || (b >= '0' && b <= '9') || b == '.' || b == '=' || b == '-'
|
||||
}
|
||||
|
||||
func isValidEscapedChar(b byte) bool {
|
||||
return b == '_' || (b >= 'a' && b <= 'z')
|
||||
}
|
||||
|
||||
// EncodeUserLocalpart encodes the given string into Matrix-compliant user ID localpart form.
|
||||
// See https://spec.matrix.org/v1.2/appendices/#mapping-from-other-character-sets
|
||||
//
|
||||
// This returns a string with only the characters "a-z0-9._=-". The uppercase range A-Z
|
||||
// are encoded using leading underscores ("_"). Characters outside the aforementioned ranges
|
||||
// (including literal underscores ("_") and equals ("=")) are encoded as UTF8 code points (NOT NCRs)
|
||||
// and converted to lower-case hex with a leading "=". For example:
|
||||
//
|
||||
// Alph@Bet_50up => _alph=40_bet=5f50up
|
||||
func EncodeUserLocalpart(str string) string {
|
||||
strBytes := []byte(str)
|
||||
var outputBuffer bytes.Buffer
|
||||
for _, b := range strBytes {
|
||||
if shouldEncode(b) {
|
||||
encode(&outputBuffer, b)
|
||||
} else if shouldEscape(b) {
|
||||
escape(&outputBuffer, b)
|
||||
} else {
|
||||
outputBuffer.WriteByte(b)
|
||||
}
|
||||
}
|
||||
return outputBuffer.String()
|
||||
}
|
||||
|
||||
// DecodeUserLocalpart decodes the given string back into the original input string.
|
||||
// Returns an error if the given string is not a valid user ID localpart encoding.
|
||||
// See https://spec.matrix.org/v1.2/appendices/#mapping-from-other-character-sets
|
||||
//
|
||||
// This decodes quoted-printable bytes back into UTF8, and unescapes casing. For
|
||||
// example:
|
||||
//
|
||||
// _alph=40_bet=5f50up => Alph@Bet_50up
|
||||
//
|
||||
// Returns an error if the input string contains characters outside the
|
||||
// range "a-z0-9._=-", has an invalid quote-printable byte (e.g. not hex), or has
|
||||
// an invalid _ escaped byte (e.g. "_5").
|
||||
func DecodeUserLocalpart(str string) (string, error) {
|
||||
strBytes := []byte(str)
|
||||
var outputBuffer bytes.Buffer
|
||||
for i := 0; i < len(strBytes); i++ {
|
||||
b := strBytes[i]
|
||||
if !isValidByte(b) {
|
||||
return "", fmt.Errorf("Byte pos %d: Invalid byte", i)
|
||||
}
|
||||
|
||||
if b == '_' { // next byte is a-z and should be upper-case or is another _ and should be a literal _
|
||||
if i+1 >= len(strBytes) {
|
||||
return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding but ran out of string", i)
|
||||
}
|
||||
if !isValidEscapedChar(strBytes[i+1]) { // invalid escaping
|
||||
return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding", i)
|
||||
}
|
||||
if strBytes[i+1] == '_' {
|
||||
outputBuffer.WriteByte('_')
|
||||
} else {
|
||||
outputBuffer.WriteByte(strBytes[i+1] - 0x20) // ASCII shift a-z to A-Z
|
||||
}
|
||||
i++ // skip next byte since we just handled it
|
||||
} else if b == '=' { // next 2 bytes are hex and should be buffered ready to be read as utf8
|
||||
if i+2 >= len(strBytes) {
|
||||
return "", fmt.Errorf("Byte pos: %d: expected quote-printable encoding but ran out of string", i)
|
||||
}
|
||||
dst := make([]byte, 1)
|
||||
_, err := hex.Decode(dst, strBytes[i+1:i+3])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
outputBuffer.WriteByte(dst[0])
|
||||
i += 2 // skip next 2 bytes since we just handled it
|
||||
} else { // pass through
|
||||
outputBuffer.WriteByte(b)
|
||||
}
|
||||
}
|
||||
return outputBuffer.String(), nil
|
||||
}
|
||||
124
vendor/maunium.net/go/mautrix/pushrules/action.go
generated
vendored
Normal file
124
vendor/maunium.net/go/mautrix/pushrules/action.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package pushrules
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// PushActionType is the type of a PushAction
|
||||
type PushActionType string
|
||||
|
||||
// The allowed push action types as specified in spec section 11.12.1.4.1.
|
||||
const (
|
||||
ActionNotify PushActionType = "notify"
|
||||
ActionDontNotify PushActionType = "dont_notify"
|
||||
ActionCoalesce PushActionType = "coalesce"
|
||||
ActionSetTweak PushActionType = "set_tweak"
|
||||
)
|
||||
|
||||
// PushActionTweak is the type of the tweak in SetTweak push actions.
|
||||
type PushActionTweak string
|
||||
|
||||
// The allowed tweak types as specified in spec section 11.12.1.4.1.1.
|
||||
const (
|
||||
TweakSound PushActionTweak = "sound"
|
||||
TweakHighlight PushActionTweak = "highlight"
|
||||
)
|
||||
|
||||
// PushActionArray is an array of PushActions.
|
||||
type PushActionArray []*PushAction
|
||||
|
||||
// PushActionArrayShould contains the important information parsed from a PushActionArray.
|
||||
type PushActionArrayShould struct {
|
||||
// Whether or not the array contained a Notify, DontNotify or Coalesce action type.
|
||||
NotifySpecified bool
|
||||
// Whether or not the event in question should trigger a notification.
|
||||
Notify bool
|
||||
// Whether or not the event in question should be highlighted.
|
||||
Highlight bool
|
||||
|
||||
// Whether or not the event in question should trigger a sound alert.
|
||||
PlaySound bool
|
||||
// The name of the sound to play if PlaySound is true.
|
||||
SoundName string
|
||||
}
|
||||
|
||||
// Should parses this push action array and returns the relevant details wrapped in a PushActionArrayShould struct.
|
||||
func (actions PushActionArray) Should() (should PushActionArrayShould) {
|
||||
for _, action := range actions {
|
||||
switch action.Action {
|
||||
case ActionNotify, ActionCoalesce:
|
||||
should.Notify = true
|
||||
should.NotifySpecified = true
|
||||
case ActionDontNotify:
|
||||
should.Notify = false
|
||||
should.NotifySpecified = true
|
||||
case ActionSetTweak:
|
||||
switch action.Tweak {
|
||||
case TweakHighlight:
|
||||
var ok bool
|
||||
should.Highlight, ok = action.Value.(bool)
|
||||
if !ok {
|
||||
// Highlight value not specified, so assume true since the tweak is set.
|
||||
should.Highlight = true
|
||||
}
|
||||
case TweakSound:
|
||||
should.SoundName = action.Value.(string)
|
||||
should.PlaySound = len(should.SoundName) > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PushAction is a single action that should be triggered when receiving a message.
|
||||
type PushAction struct {
|
||||
Action PushActionType
|
||||
Tweak PushActionTweak
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses JSON into this PushAction.
|
||||
//
|
||||
// - If the JSON is a single string, the value is stored in the Action field.
|
||||
// - If the JSON is an object with the set_tweak field, Action will be set to
|
||||
// "set_tweak", Tweak will be set to the value of the set_tweak field and
|
||||
// and Value will be set to the value of the value field.
|
||||
// - In any other case, the function does nothing.
|
||||
func (action *PushAction) UnmarshalJSON(raw []byte) error {
|
||||
var data interface{}
|
||||
|
||||
err := json.Unmarshal(raw, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch val := data.(type) {
|
||||
case string:
|
||||
action.Action = PushActionType(val)
|
||||
case map[string]interface{}:
|
||||
tweak, ok := val["set_tweak"].(string)
|
||||
if ok {
|
||||
action.Action = ActionSetTweak
|
||||
action.Tweak = PushActionTweak(tweak)
|
||||
action.Value, _ = val["value"]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON is the reverse of UnmarshalJSON()
|
||||
func (action *PushAction) MarshalJSON() (raw []byte, err error) {
|
||||
if action.Action == ActionSetTweak {
|
||||
data := map[string]interface{}{
|
||||
"set_tweak": action.Tweak,
|
||||
"value": action.Value,
|
||||
}
|
||||
return json.Marshal(&data)
|
||||
}
|
||||
data := string(action.Action)
|
||||
return json.Marshal(&data)
|
||||
}
|
||||
266
vendor/maunium.net/go/mautrix/pushrules/condition.go
generated
vendored
Normal file
266
vendor/maunium.net/go/mautrix/pushrules/condition.go
generated
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package pushrules
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/mautrix/pushrules/glob"
|
||||
)
|
||||
|
||||
// Room is an interface with the functions that are needed for processing room-specific push conditions
|
||||
type Room interface {
|
||||
GetOwnDisplayname() string
|
||||
GetMemberCount() int
|
||||
}
|
||||
|
||||
// EventfulRoom is an extension of Room to support MSC3664.
|
||||
type EventfulRoom interface {
|
||||
Room
|
||||
GetEvent(id.EventID) *event.Event
|
||||
}
|
||||
|
||||
// PushCondKind is the type of a push condition.
|
||||
type PushCondKind string
|
||||
|
||||
// The allowed push condition kinds as specified in https://spec.matrix.org/v1.2/client-server-api/#conditions-1
|
||||
const (
|
||||
KindEventMatch PushCondKind = "event_match"
|
||||
KindContainsDisplayName PushCondKind = "contains_display_name"
|
||||
KindRoomMemberCount PushCondKind = "room_member_count"
|
||||
|
||||
// MSC3664: https://github.com/matrix-org/matrix-spec-proposals/pull/3664
|
||||
|
||||
KindRelatedEventMatch PushCondKind = "related_event_match"
|
||||
KindUnstableRelatedEventMatch PushCondKind = "im.nheko.msc3664.related_event_match"
|
||||
)
|
||||
|
||||
// PushCondition wraps a condition that is required for a specific PushRule to be used.
|
||||
type PushCondition struct {
|
||||
// The type of the condition.
|
||||
Kind PushCondKind `json:"kind"`
|
||||
// The dot-separated field of the event to match. Only applicable if kind is EventMatch.
|
||||
Key string `json:"key,omitempty"`
|
||||
// The glob-style pattern to match the field against. Only applicable if kind is EventMatch.
|
||||
Pattern string `json:"pattern,omitempty"`
|
||||
// The condition that needs to be fulfilled for RoomMemberCount-type conditions.
|
||||
// A decimal integer optionally prefixed by ==, <, >, >= or <=. Prefix "==" is assumed if no prefix found.
|
||||
MemberCountCondition string `json:"is,omitempty"`
|
||||
|
||||
// The relation type for related_event_match from MSC3664
|
||||
RelType event.RelationType `json:"rel_type,omitempty"`
|
||||
}
|
||||
|
||||
// MemberCountFilterRegex is the regular expression to parse the MemberCountCondition of PushConditions.
|
||||
var MemberCountFilterRegex = regexp.MustCompile("^(==|[<>]=?)?([0-9]+)$")
|
||||
|
||||
// Match checks if this condition is fulfilled for the given event in the given room.
|
||||
func (cond *PushCondition) Match(room Room, evt *event.Event) bool {
|
||||
switch cond.Kind {
|
||||
case KindEventMatch:
|
||||
return cond.matchValue(room, evt)
|
||||
case KindRelatedEventMatch, KindUnstableRelatedEventMatch:
|
||||
return cond.matchRelatedEvent(room, evt)
|
||||
case KindContainsDisplayName:
|
||||
return cond.matchDisplayName(room, evt)
|
||||
case KindRoomMemberCount:
|
||||
return cond.matchMemberCount(room)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func splitWithEscaping(s string, separator, escape byte) []string {
|
||||
var token []byte
|
||||
var tokens []string
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == separator {
|
||||
tokens = append(tokens, string(token))
|
||||
token = token[:0]
|
||||
} else if s[i] == escape && i+1 < len(s) {
|
||||
i++
|
||||
token = append(token, s[i])
|
||||
} else {
|
||||
token = append(token, s[i])
|
||||
}
|
||||
}
|
||||
tokens = append(tokens, string(token))
|
||||
return tokens
|
||||
}
|
||||
|
||||
func hackyNestedGet(data map[string]interface{}, path []string) (interface{}, bool) {
|
||||
val, ok := data[path[0]]
|
||||
if len(path) == 1 {
|
||||
// We don't have any more path parts, return the value regardless of whether it exists or not.
|
||||
return val, ok
|
||||
} else if ok {
|
||||
if mapVal, ok := val.(map[string]interface{}); ok {
|
||||
val, ok = hackyNestedGet(mapVal, path[1:])
|
||||
if ok {
|
||||
return val, true
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we don't find the key, try to combine the first two parts.
|
||||
// e.g. if the key is content.m.relates_to.rel_type, we'll first try data["m"], which will fail,
|
||||
// then combine m and relates_to to get data["m.relates_to"], which should succeed.
|
||||
path[1] = path[0] + "." + path[1]
|
||||
return hackyNestedGet(data, path[1:])
|
||||
}
|
||||
|
||||
func stringifyForPushCondition(val interface{}) string {
|
||||
// Implement MSC3862 to allow matching any type of field
|
||||
// https://github.com/matrix-org/matrix-spec-proposals/pull/3862
|
||||
switch typedVal := val.(type) {
|
||||
case string:
|
||||
return typedVal
|
||||
case nil:
|
||||
return "null"
|
||||
case float64:
|
||||
// Floats aren't allowed in Matrix events, but the JSON parser always stores numbers as floats,
|
||||
// so just handle that and convert to int
|
||||
return strconv.FormatInt(int64(typedVal), 10)
|
||||
default:
|
||||
return fmt.Sprint(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (cond *PushCondition) matchValue(room Room, evt *event.Event) bool {
|
||||
key, subkey, _ := strings.Cut(cond.Key, ".")
|
||||
|
||||
pattern, err := glob.Compile(cond.Pattern)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "type":
|
||||
return pattern.MatchString(evt.Type.String())
|
||||
case "sender":
|
||||
return pattern.MatchString(string(evt.Sender))
|
||||
case "room_id":
|
||||
return pattern.MatchString(string(evt.RoomID))
|
||||
case "state_key":
|
||||
if evt.StateKey == nil {
|
||||
return false
|
||||
}
|
||||
return pattern.MatchString(*evt.StateKey)
|
||||
case "content":
|
||||
// Split the match key with escaping to implement https://github.com/matrix-org/matrix-spec-proposals/pull/3873
|
||||
splitKey := splitWithEscaping(subkey, '.', '\\')
|
||||
// Then do a hacky nested get that supports combining parts for the backwards-compat part of MSC3873
|
||||
val, ok := hackyNestedGet(evt.Content.Raw, splitKey)
|
||||
if !ok {
|
||||
return cond.Pattern == ""
|
||||
}
|
||||
return pattern.MatchString(stringifyForPushCondition(val))
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (cond *PushCondition) getRelationEventID(relatesTo *event.RelatesTo) id.EventID {
|
||||
if relatesTo == nil {
|
||||
return ""
|
||||
}
|
||||
switch cond.RelType {
|
||||
case "":
|
||||
return relatesTo.EventID
|
||||
case "m.in_reply_to":
|
||||
if relatesTo.IsFallingBack || relatesTo.InReplyTo == nil {
|
||||
return ""
|
||||
}
|
||||
return relatesTo.InReplyTo.EventID
|
||||
default:
|
||||
if relatesTo.Type != cond.RelType {
|
||||
return ""
|
||||
}
|
||||
return relatesTo.EventID
|
||||
}
|
||||
}
|
||||
|
||||
func (cond *PushCondition) matchRelatedEvent(room Room, evt *event.Event) bool {
|
||||
var relatesTo *event.RelatesTo
|
||||
if relatable, ok := evt.Content.Parsed.(event.Relatable); ok {
|
||||
relatesTo = relatable.OptionalGetRelatesTo()
|
||||
} else {
|
||||
res := gjson.GetBytes(evt.Content.VeryRaw, `m\.relates_to`)
|
||||
if res.Exists() && res.IsObject() {
|
||||
_ = json.Unmarshal([]byte(res.Raw), &relatesTo)
|
||||
}
|
||||
}
|
||||
if evtID := cond.getRelationEventID(relatesTo); evtID == "" {
|
||||
return false
|
||||
} else if eventfulRoom, ok := room.(EventfulRoom); !ok {
|
||||
return false
|
||||
} else if evt = eventfulRoom.GetEvent(relatesTo.EventID); evt == nil {
|
||||
return false
|
||||
} else {
|
||||
return cond.matchValue(room, evt)
|
||||
}
|
||||
}
|
||||
|
||||
func (cond *PushCondition) matchDisplayName(room Room, evt *event.Event) bool {
|
||||
displayname := room.GetOwnDisplayname()
|
||||
if len(displayname) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
msg, ok := evt.Content.Raw["body"].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
isAcceptable := func(r uint8) bool {
|
||||
return unicode.IsSpace(rune(r)) || unicode.IsPunct(rune(r))
|
||||
}
|
||||
length := len(displayname)
|
||||
for index := strings.Index(msg, displayname); index != -1; index = strings.Index(msg, displayname) {
|
||||
if (index <= 0 || isAcceptable(msg[index-1])) && (index+length >= len(msg) || isAcceptable(msg[index+length])) {
|
||||
return true
|
||||
}
|
||||
msg = msg[index+len(displayname):]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (cond *PushCondition) matchMemberCount(room Room) bool {
|
||||
group := MemberCountFilterRegex.FindStringSubmatch(cond.MemberCountCondition)
|
||||
if len(group) != 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
operator := group[1]
|
||||
wantedMemberCount, _ := strconv.Atoi(group[2])
|
||||
|
||||
memberCount := room.GetMemberCount()
|
||||
|
||||
switch operator {
|
||||
case "==", "":
|
||||
return memberCount == wantedMemberCount
|
||||
case ">":
|
||||
return memberCount > wantedMemberCount
|
||||
case ">=":
|
||||
return memberCount >= wantedMemberCount
|
||||
case "<":
|
||||
return memberCount < wantedMemberCount
|
||||
case "<=":
|
||||
return memberCount <= wantedMemberCount
|
||||
default:
|
||||
// Should be impossible due to regex.
|
||||
return false
|
||||
}
|
||||
}
|
||||
2
vendor/maunium.net/go/mautrix/pushrules/doc.go
generated
vendored
Normal file
2
vendor/maunium.net/go/mautrix/pushrules/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package pushrules contains utilities to parse push notification rules.
|
||||
package pushrules
|
||||
22
vendor/maunium.net/go/mautrix/pushrules/glob/LICENSE
generated
vendored
Normal file
22
vendor/maunium.net/go/mautrix/pushrules/glob/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Glob is licensed under the MIT "Expat" License:
|
||||
|
||||
Copyright (c) 2016: Zachary Yedidia.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
28
vendor/maunium.net/go/mautrix/pushrules/glob/README.md
generated
vendored
Normal file
28
vendor/maunium.net/go/mautrix/pushrules/glob/README.md
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# String globbing in Go
|
||||
|
||||
[](http://godoc.org/github.com/zyedidia/glob)
|
||||
|
||||
This package adds support for globs in Go.
|
||||
|
||||
It simply converts glob expressions to regexps. I try to follow the standard defined [here](http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13).
|
||||
|
||||
# Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "github.com/zyedidia/glob"
|
||||
|
||||
func main() {
|
||||
glob, err := glob.Compile("{*.go,*.c}")
|
||||
if err != nil {
|
||||
// Error
|
||||
}
|
||||
|
||||
glob.Match([]byte("test.c")) // true
|
||||
glob.Match([]byte("hello.go")) // true
|
||||
glob.Match([]byte("test.d")) // false
|
||||
}
|
||||
```
|
||||
|
||||
You can call all the same functions on a glob that you can call on a regexp.
|
||||
108
vendor/maunium.net/go/mautrix/pushrules/glob/glob.go
generated
vendored
Normal file
108
vendor/maunium.net/go/mautrix/pushrules/glob/glob.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// Package glob provides objects for matching strings with globs
|
||||
package glob
|
||||
|
||||
import "regexp"
|
||||
|
||||
// Glob is a wrapper of *regexp.Regexp.
|
||||
// It should contain a glob expression compiled into a regular expression.
|
||||
type Glob struct {
|
||||
*regexp.Regexp
|
||||
}
|
||||
|
||||
// Compile a takes a glob expression as a string and transforms it
|
||||
// into a *Glob object (which is really just a regular expression)
|
||||
// Compile also returns a possible error.
|
||||
func Compile(pattern string) (*Glob, error) {
|
||||
r, err := globToRegex(pattern)
|
||||
return &Glob{r}, err
|
||||
}
|
||||
|
||||
func globToRegex(glob string) (*regexp.Regexp, error) {
|
||||
regex := ""
|
||||
inGroup := 0
|
||||
inClass := 0
|
||||
firstIndexInClass := -1
|
||||
arr := []byte(glob)
|
||||
|
||||
hasGlobCharacters := false
|
||||
|
||||
for i := 0; i < len(arr); i++ {
|
||||
ch := arr[i]
|
||||
|
||||
switch ch {
|
||||
case '\\':
|
||||
i++
|
||||
if i >= len(arr) {
|
||||
regex += "\\"
|
||||
} else {
|
||||
next := arr[i]
|
||||
switch next {
|
||||
case ',':
|
||||
// Nothing
|
||||
case 'Q', 'E':
|
||||
regex += "\\\\"
|
||||
default:
|
||||
regex += "\\"
|
||||
}
|
||||
regex += string(next)
|
||||
}
|
||||
case '*':
|
||||
if inClass == 0 {
|
||||
regex += ".*"
|
||||
} else {
|
||||
regex += "*"
|
||||
}
|
||||
hasGlobCharacters = true
|
||||
case '?':
|
||||
if inClass == 0 {
|
||||
regex += "."
|
||||
} else {
|
||||
regex += "?"
|
||||
}
|
||||
hasGlobCharacters = true
|
||||
case '[':
|
||||
inClass++
|
||||
firstIndexInClass = i + 1
|
||||
regex += "["
|
||||
hasGlobCharacters = true
|
||||
case ']':
|
||||
inClass--
|
||||
regex += "]"
|
||||
case '.', '(', ')', '+', '|', '^', '$', '@', '%':
|
||||
if inClass == 0 || (firstIndexInClass == i && ch == '^') {
|
||||
regex += "\\"
|
||||
}
|
||||
regex += string(ch)
|
||||
hasGlobCharacters = true
|
||||
case '!':
|
||||
if firstIndexInClass == i {
|
||||
regex += "^"
|
||||
} else {
|
||||
regex += "!"
|
||||
}
|
||||
hasGlobCharacters = true
|
||||
case '{':
|
||||
inGroup++
|
||||
regex += "("
|
||||
hasGlobCharacters = true
|
||||
case '}':
|
||||
inGroup--
|
||||
regex += ")"
|
||||
case ',':
|
||||
if inGroup > 0 {
|
||||
regex += "|"
|
||||
hasGlobCharacters = true
|
||||
} else {
|
||||
regex += ","
|
||||
}
|
||||
default:
|
||||
regex += string(ch)
|
||||
}
|
||||
}
|
||||
|
||||
if hasGlobCharacters {
|
||||
return regexp.Compile("^" + regex + "$")
|
||||
} else {
|
||||
return regexp.Compile(regex)
|
||||
}
|
||||
}
|
||||
37
vendor/maunium.net/go/mautrix/pushrules/pushrules.go
generated
vendored
Normal file
37
vendor/maunium.net/go/mautrix/pushrules/pushrules.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package pushrules
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
)
|
||||
|
||||
// EventContent represents the content of a m.push_rules account data event.
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#mpush_rules
|
||||
type EventContent struct {
|
||||
Ruleset *PushRuleset `json:"global"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
event.TypeMap[event.AccountDataPushRules] = reflect.TypeOf(EventContent{})
|
||||
gob.Register(&EventContent{})
|
||||
}
|
||||
|
||||
// EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON.
|
||||
func EventToPushRules(evt *event.Event) (*PushRuleset, error) {
|
||||
content := &EventContent{}
|
||||
err := json.Unmarshal(evt.Content.VeryRaw, content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return content.Ruleset, nil
|
||||
}
|
||||
154
vendor/maunium.net/go/mautrix/pushrules/rule.go
generated
vendored
Normal file
154
vendor/maunium.net/go/mautrix/pushrules/rule.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package pushrules
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/mautrix/pushrules/glob"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(PushRuleArray{})
|
||||
gob.Register(PushRuleMap{})
|
||||
}
|
||||
|
||||
type PushRuleCollection interface {
|
||||
GetActions(room Room, evt *event.Event) PushActionArray
|
||||
}
|
||||
|
||||
type PushRuleArray []*PushRule
|
||||
|
||||
func (rules PushRuleArray) SetType(typ PushRuleType) PushRuleArray {
|
||||
for _, rule := range rules {
|
||||
rule.Type = typ
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
func (rules PushRuleArray) GetActions(room Room, evt *event.Event) PushActionArray {
|
||||
for _, rule := range rules {
|
||||
if !rule.Match(room, evt) {
|
||||
continue
|
||||
}
|
||||
return rule.Actions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PushRuleMap struct {
|
||||
Map map[string]*PushRule
|
||||
Type PushRuleType
|
||||
}
|
||||
|
||||
func (rules PushRuleArray) SetTypeAndMap(typ PushRuleType) PushRuleMap {
|
||||
data := PushRuleMap{
|
||||
Map: make(map[string]*PushRule),
|
||||
Type: typ,
|
||||
}
|
||||
for _, rule := range rules {
|
||||
rule.Type = typ
|
||||
data.Map[rule.RuleID] = rule
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (ruleMap PushRuleMap) GetActions(room Room, evt *event.Event) PushActionArray {
|
||||
var rule *PushRule
|
||||
var found bool
|
||||
switch ruleMap.Type {
|
||||
case RoomRule:
|
||||
rule, found = ruleMap.Map[string(evt.RoomID)]
|
||||
case SenderRule:
|
||||
rule, found = ruleMap.Map[string(evt.Sender)]
|
||||
}
|
||||
if found && rule.Match(room, evt) {
|
||||
return rule.Actions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ruleMap PushRuleMap) Unmap() PushRuleArray {
|
||||
array := make(PushRuleArray, len(ruleMap.Map))
|
||||
index := 0
|
||||
for _, rule := range ruleMap.Map {
|
||||
array[index] = rule
|
||||
index++
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
type PushRuleType string
|
||||
|
||||
const (
|
||||
OverrideRule PushRuleType = "override"
|
||||
ContentRule PushRuleType = "content"
|
||||
RoomRule PushRuleType = "room"
|
||||
SenderRule PushRuleType = "sender"
|
||||
UnderrideRule PushRuleType = "underride"
|
||||
)
|
||||
|
||||
type PushRule struct {
|
||||
// The type of this rule.
|
||||
Type PushRuleType `json:"-"`
|
||||
// The ID of this rule.
|
||||
// For room-specific rules and user-specific rules, this is the room or user ID (respectively)
|
||||
// For other types of rules, this doesn't affect anything.
|
||||
RuleID string `json:"rule_id"`
|
||||
// The actions this rule should trigger when matched.
|
||||
Actions PushActionArray `json:"actions"`
|
||||
// Whether this is a default rule, or has been set explicitly.
|
||||
Default bool `json:"default"`
|
||||
// Whether or not this push rule is enabled.
|
||||
Enabled bool `json:"enabled"`
|
||||
// The conditions to match in order to trigger this rule.
|
||||
// Only applicable to generic underride/override rules.
|
||||
Conditions []*PushCondition `json:"conditions,omitempty"`
|
||||
// Pattern for content-specific push rules
|
||||
Pattern string `json:"pattern,omitempty"`
|
||||
}
|
||||
|
||||
func (rule *PushRule) Match(room Room, evt *event.Event) bool {
|
||||
if !rule.Enabled {
|
||||
return false
|
||||
}
|
||||
switch rule.Type {
|
||||
case OverrideRule, UnderrideRule:
|
||||
return rule.matchConditions(room, evt)
|
||||
case ContentRule:
|
||||
return rule.matchPattern(room, evt)
|
||||
case RoomRule:
|
||||
return id.RoomID(rule.RuleID) == evt.RoomID
|
||||
case SenderRule:
|
||||
return id.UserID(rule.RuleID) == evt.Sender
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (rule *PushRule) matchConditions(room Room, evt *event.Event) bool {
|
||||
for _, cond := range rule.Conditions {
|
||||
if !cond.Match(room, evt) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (rule *PushRule) matchPattern(room Room, evt *event.Event) bool {
|
||||
pattern, err := glob.Compile(rule.Pattern)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
msg, ok := evt.Content.Raw["body"].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return pattern.MatchString(msg)
|
||||
}
|
||||
88
vendor/maunium.net/go/mautrix/pushrules/ruleset.go
generated
vendored
Normal file
88
vendor/maunium.net/go/mautrix/pushrules/ruleset.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package pushrules
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
)
|
||||
|
||||
type PushRuleset struct {
|
||||
Override PushRuleArray
|
||||
Content PushRuleArray
|
||||
Room PushRuleMap
|
||||
Sender PushRuleMap
|
||||
Underride PushRuleArray
|
||||
}
|
||||
|
||||
type rawPushRuleset struct {
|
||||
Override PushRuleArray `json:"override"`
|
||||
Content PushRuleArray `json:"content"`
|
||||
Room PushRuleArray `json:"room"`
|
||||
Sender PushRuleArray `json:"sender"`
|
||||
Underride PushRuleArray `json:"underride"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses JSON into this PushRuleset.
|
||||
//
|
||||
// For override, sender and underride push rule arrays, the type is added
|
||||
// to each PushRule and the array is used as-is.
|
||||
//
|
||||
// For room and sender push rule arrays, the type is added to each PushRule
|
||||
// and the array is converted to a map with the rule ID as the key and the
|
||||
// PushRule as the value.
|
||||
func (rs *PushRuleset) UnmarshalJSON(raw []byte) (err error) {
|
||||
data := rawPushRuleset{}
|
||||
err = json.Unmarshal(raw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rs.Override = data.Override.SetType(OverrideRule)
|
||||
rs.Content = data.Content.SetType(ContentRule)
|
||||
rs.Room = data.Room.SetTypeAndMap(RoomRule)
|
||||
rs.Sender = data.Sender.SetTypeAndMap(SenderRule)
|
||||
rs.Underride = data.Underride.SetType(UnderrideRule)
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalJSON is the reverse of UnmarshalJSON()
|
||||
func (rs *PushRuleset) MarshalJSON() ([]byte, error) {
|
||||
data := rawPushRuleset{
|
||||
Override: rs.Override,
|
||||
Content: rs.Content,
|
||||
Room: rs.Room.Unmap(),
|
||||
Sender: rs.Sender.Unmap(),
|
||||
Underride: rs.Underride,
|
||||
}
|
||||
return json.Marshal(&data)
|
||||
}
|
||||
|
||||
// DefaultPushActions is the value returned if none of the rule
|
||||
// collections in a Ruleset match the event given to GetActions()
|
||||
var DefaultPushActions = PushActionArray{&PushAction{Action: ActionDontNotify}}
|
||||
|
||||
// GetActions matches the given event against all of the push rule
|
||||
// collections in this push ruleset in the order of priority as
|
||||
// specified in spec section 11.12.1.4.
|
||||
func (rs *PushRuleset) GetActions(room Room, evt *event.Event) (match PushActionArray) {
|
||||
// Add push rule collections to array in priority order
|
||||
arrays := []PushRuleCollection{rs.Override, rs.Content, rs.Room, rs.Sender, rs.Underride}
|
||||
// Loop until one of the push rule collections matches the room/event combo.
|
||||
for _, pra := range arrays {
|
||||
if pra == nil {
|
||||
continue
|
||||
}
|
||||
if match = pra.GetActions(room, evt); match != nil {
|
||||
// Match found, return it.
|
||||
return
|
||||
}
|
||||
}
|
||||
// No match found, return default actions.
|
||||
return DefaultPushActions
|
||||
}
|
||||
437
vendor/maunium.net/go/mautrix/requests.go
generated
vendored
Normal file
437
vendor/maunium.net/go/mautrix/requests.go
generated
vendored
Normal file
@@ -0,0 +1,437 @@
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/mautrix/pushrules"
|
||||
)
|
||||
|
||||
type AuthType string
|
||||
|
||||
const (
|
||||
AuthTypePassword AuthType = "m.login.password"
|
||||
AuthTypeReCAPTCHA AuthType = "m.login.recaptcha"
|
||||
AuthTypeOAuth2 AuthType = "m.login.oauth2"
|
||||
AuthTypeSSO AuthType = "m.login.sso"
|
||||
AuthTypeEmail AuthType = "m.login.email.identity"
|
||||
AuthTypeMSISDN AuthType = "m.login.msisdn"
|
||||
AuthTypeToken AuthType = "m.login.token"
|
||||
AuthTypeDummy AuthType = "m.login.dummy"
|
||||
AuthTypeAppservice AuthType = "m.login.application_service"
|
||||
)
|
||||
|
||||
type IdentifierType string
|
||||
|
||||
const (
|
||||
IdentifierTypeUser = "m.id.user"
|
||||
IdentifierTypeThirdParty = "m.id.thirdparty"
|
||||
IdentifierTypePhone = "m.id.phone"
|
||||
)
|
||||
|
||||
type Direction rune
|
||||
|
||||
const (
|
||||
DirectionForward Direction = 'f'
|
||||
DirectionBackward Direction = 'b'
|
||||
)
|
||||
|
||||
// ReqRegister is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3register
|
||||
type ReqRegister struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
DeviceID id.DeviceID `json:"device_id,omitempty"`
|
||||
InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"`
|
||||
InhibitLogin bool `json:"inhibit_login,omitempty"`
|
||||
RefreshToken bool `json:"refresh_token,omitempty"`
|
||||
Auth interface{} `json:"auth,omitempty"`
|
||||
|
||||
// Type for registration, only used for appservice user registrations
|
||||
// https://spec.matrix.org/v1.2/application-service-api/#server-admin-style-permissions
|
||||
Type AuthType `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type BaseAuthData struct {
|
||||
Type AuthType `json:"type"`
|
||||
Session string `json:"session,omitempty"`
|
||||
}
|
||||
|
||||
type UserIdentifier struct {
|
||||
Type IdentifierType `json:"type"`
|
||||
|
||||
User string `json:"user,omitempty"`
|
||||
|
||||
Medium string `json:"medium,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
|
||||
Country string `json:"country,omitempty"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
}
|
||||
|
||||
// ReqLogin is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3login
|
||||
type ReqLogin struct {
|
||||
Type AuthType `json:"type"`
|
||||
Identifier UserIdentifier `json:"identifier"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
DeviceID id.DeviceID `json:"device_id,omitempty"`
|
||||
InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"`
|
||||
|
||||
// Whether or not the returned credentials should be stored in the Client
|
||||
StoreCredentials bool `json:"-"`
|
||||
// Whether or not the returned .well-known data should update the homeserver URL in the Client
|
||||
StoreHomeserverURL bool `json:"-"`
|
||||
}
|
||||
|
||||
type ReqUIAuthFallback struct {
|
||||
Session string `json:"session"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
type ReqUIAuthLogin struct {
|
||||
BaseAuthData
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// ReqCreateRoom is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom
|
||||
type ReqCreateRoom struct {
|
||||
Visibility string `json:"visibility,omitempty"`
|
||||
RoomAliasName string `json:"room_alias_name,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Topic string `json:"topic,omitempty"`
|
||||
Invite []id.UserID `json:"invite,omitempty"`
|
||||
Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"`
|
||||
CreationContent map[string]interface{} `json:"creation_content,omitempty"`
|
||||
InitialState []*event.Event `json:"initial_state,omitempty"`
|
||||
Preset string `json:"preset,omitempty"`
|
||||
IsDirect bool `json:"is_direct,omitempty"`
|
||||
RoomVersion string `json:"room_version,omitempty"`
|
||||
|
||||
PowerLevelOverride *event.PowerLevelsEventContent `json:"power_level_content_override,omitempty"`
|
||||
|
||||
MeowRoomID id.RoomID `json:"fi.mau.room_id,omitempty"`
|
||||
BeeperAutoJoinInvites bool `json:"com.beeper.auto_join_invites,omitempty"`
|
||||
}
|
||||
|
||||
// ReqRedact is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidredacteventidtxnid
|
||||
type ReqRedact struct {
|
||||
Reason string
|
||||
TxnID string
|
||||
Extra map[string]interface{}
|
||||
}
|
||||
|
||||
type ReqMembers struct {
|
||||
At string `json:"at"`
|
||||
Membership event.Membership `json:"membership,omitempty"`
|
||||
NotMembership event.Membership `json:"not_membership,omitempty"`
|
||||
}
|
||||
|
||||
// ReqInvite3PID is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite-1
|
||||
// It is also a JSON object used in https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom
|
||||
type ReqInvite3PID struct {
|
||||
IDServer string `json:"id_server"`
|
||||
Medium string `json:"medium"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type ReqLeave struct {
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// ReqInviteUser is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite
|
||||
type ReqInviteUser struct {
|
||||
Reason string `json:"reason,omitempty"`
|
||||
UserID id.UserID `json:"user_id"`
|
||||
}
|
||||
|
||||
// ReqKickUser is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidkick
|
||||
type ReqKickUser struct {
|
||||
Reason string `json:"reason,omitempty"`
|
||||
UserID id.UserID `json:"user_id"`
|
||||
}
|
||||
|
||||
// ReqBanUser is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidban
|
||||
type ReqBanUser struct {
|
||||
Reason string `json:"reason,omitempty"`
|
||||
UserID id.UserID `json:"user_id"`
|
||||
}
|
||||
|
||||
// ReqUnbanUser is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidunban
|
||||
type ReqUnbanUser struct {
|
||||
Reason string `json:"reason,omitempty"`
|
||||
UserID id.UserID `json:"user_id"`
|
||||
}
|
||||
|
||||
// ReqTyping is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidtypinguserid
|
||||
type ReqTyping struct {
|
||||
Typing bool `json:"typing"`
|
||||
Timeout int64 `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type ReqPresence struct {
|
||||
Presence event.Presence `json:"presence"`
|
||||
}
|
||||
|
||||
type ReqAliasCreate struct {
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
}
|
||||
|
||||
type OneTimeKey struct {
|
||||
Key id.Curve25519 `json:"key"`
|
||||
Fallback bool `json:"fallback,omitempty"`
|
||||
Signatures Signatures `json:"signatures,omitempty"`
|
||||
Unsigned map[string]any `json:"unsigned,omitempty"`
|
||||
IsSigned bool `json:"-"`
|
||||
|
||||
// Raw data in the one-time key. This must be used for signature verification to ensure unrecognized fields
|
||||
// aren't thrown away (because that would invalidate the signature).
|
||||
RawData json.RawMessage `json:"-"`
|
||||
}
|
||||
|
||||
type serializableOTK OneTimeKey
|
||||
|
||||
func (otk *OneTimeKey) UnmarshalJSON(data []byte) (err error) {
|
||||
if len(data) > 0 && data[0] == '"' && data[len(data)-1] == '"' {
|
||||
err = json.Unmarshal(data, &otk.Key)
|
||||
otk.Signatures = nil
|
||||
otk.Unsigned = nil
|
||||
otk.IsSigned = false
|
||||
} else {
|
||||
err = json.Unmarshal(data, (*serializableOTK)(otk))
|
||||
otk.RawData = data
|
||||
otk.IsSigned = true
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (otk *OneTimeKey) MarshalJSON() ([]byte, error) {
|
||||
if !otk.IsSigned {
|
||||
return json.Marshal(otk.Key)
|
||||
} else {
|
||||
return json.Marshal((*serializableOTK)(otk))
|
||||
}
|
||||
}
|
||||
|
||||
type ReqUploadKeys struct {
|
||||
DeviceKeys *DeviceKeys `json:"device_keys,omitempty"`
|
||||
OneTimeKeys map[id.KeyID]OneTimeKey `json:"one_time_keys"`
|
||||
}
|
||||
|
||||
type ReqKeysSignatures struct {
|
||||
UserID id.UserID `json:"user_id"`
|
||||
DeviceID id.DeviceID `json:"device_id,omitempty"`
|
||||
Algorithms []id.Algorithm `json:"algorithms,omitempty"`
|
||||
Usage []id.CrossSigningUsage `json:"usage,omitempty"`
|
||||
Keys map[id.KeyID]string `json:"keys"`
|
||||
Signatures Signatures `json:"signatures"`
|
||||
}
|
||||
|
||||
type ReqUploadSignatures map[id.UserID]map[string]ReqKeysSignatures
|
||||
|
||||
type DeviceKeys struct {
|
||||
UserID id.UserID `json:"user_id"`
|
||||
DeviceID id.DeviceID `json:"device_id"`
|
||||
Algorithms []id.Algorithm `json:"algorithms"`
|
||||
Keys KeyMap `json:"keys"`
|
||||
Signatures Signatures `json:"signatures"`
|
||||
Unsigned map[string]interface{} `json:"unsigned,omitempty"`
|
||||
}
|
||||
|
||||
type CrossSigningKeys struct {
|
||||
UserID id.UserID `json:"user_id"`
|
||||
Usage []id.CrossSigningUsage `json:"usage"`
|
||||
Keys map[id.KeyID]id.Ed25519 `json:"keys"`
|
||||
Signatures map[id.UserID]map[id.KeyID]string `json:"signatures,omitempty"`
|
||||
}
|
||||
|
||||
func (csk *CrossSigningKeys) FirstKey() id.Ed25519 {
|
||||
for _, key := range csk.Keys {
|
||||
return key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type UploadCrossSigningKeysReq struct {
|
||||
Master CrossSigningKeys `json:"master_key"`
|
||||
SelfSigning CrossSigningKeys `json:"self_signing_key"`
|
||||
UserSigning CrossSigningKeys `json:"user_signing_key"`
|
||||
Auth interface{} `json:"auth,omitempty"`
|
||||
}
|
||||
|
||||
type KeyMap map[id.DeviceKeyID]string
|
||||
|
||||
func (km KeyMap) GetEd25519(deviceID id.DeviceID) id.Ed25519 {
|
||||
val, ok := km[id.NewDeviceKeyID(id.KeyAlgorithmEd25519, deviceID)]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return id.Ed25519(val)
|
||||
}
|
||||
|
||||
func (km KeyMap) GetCurve25519(deviceID id.DeviceID) id.Curve25519 {
|
||||
val, ok := km[id.NewDeviceKeyID(id.KeyAlgorithmCurve25519, deviceID)]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return id.Curve25519(val)
|
||||
}
|
||||
|
||||
type Signatures map[id.UserID]map[id.KeyID]string
|
||||
|
||||
type ReqQueryKeys struct {
|
||||
DeviceKeys DeviceKeysRequest `json:"device_keys"`
|
||||
|
||||
Timeout int64 `json:"timeout,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
}
|
||||
|
||||
type DeviceKeysRequest map[id.UserID]DeviceIDList
|
||||
|
||||
type DeviceIDList []id.DeviceID
|
||||
|
||||
type ReqClaimKeys struct {
|
||||
OneTimeKeys OneTimeKeysRequest `json:"one_time_keys"`
|
||||
|
||||
Timeout int64 `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type OneTimeKeysRequest map[id.UserID]map[id.DeviceID]id.KeyAlgorithm
|
||||
|
||||
type ReqSendToDevice struct {
|
||||
Messages map[id.UserID]map[id.DeviceID]*event.Content `json:"messages"`
|
||||
}
|
||||
|
||||
// ReqDeviceInfo is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3devicesdeviceid
|
||||
type ReqDeviceInfo struct {
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
}
|
||||
|
||||
// ReqDeleteDevice is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#delete_matrixclientv3devicesdeviceid
|
||||
type ReqDeleteDevice struct {
|
||||
Auth interface{} `json:"auth,omitempty"`
|
||||
}
|
||||
|
||||
// ReqDeleteDevices is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3delete_devices
|
||||
type ReqDeleteDevices struct {
|
||||
Devices []id.DeviceID `json:"devices"`
|
||||
Auth interface{} `json:"auth,omitempty"`
|
||||
}
|
||||
|
||||
type ReqPutPushRule struct {
|
||||
Before string `json:"-"`
|
||||
After string `json:"-"`
|
||||
|
||||
Actions []pushrules.PushActionType `json:"actions"`
|
||||
Conditions []pushrules.PushCondition `json:"conditions"`
|
||||
Pattern string `json:"pattern"`
|
||||
}
|
||||
|
||||
type ReqBatchSend struct {
|
||||
PrevEventID id.EventID `json:"-"`
|
||||
BatchID id.BatchID `json:"-"`
|
||||
|
||||
BeeperNewMessages bool `json:"-"`
|
||||
BeeperMarkReadBy id.UserID `json:"-"`
|
||||
|
||||
StateEventsAtStart []*event.Event `json:"state_events_at_start"`
|
||||
Events []*event.Event `json:"events"`
|
||||
}
|
||||
|
||||
type ReqSetReadMarkers struct {
|
||||
Read id.EventID `json:"m.read,omitempty"`
|
||||
ReadPrivate id.EventID `json:"m.read.private,omitempty"`
|
||||
FullyRead id.EventID `json:"m.fully_read,omitempty"`
|
||||
|
||||
BeeperReadExtra interface{} `json:"com.beeper.read.extra,omitempty"`
|
||||
BeeperReadPrivateExtra interface{} `json:"com.beeper.read.private.extra,omitempty"`
|
||||
BeeperFullyReadExtra interface{} `json:"com.beeper.fully_read.extra,omitempty"`
|
||||
}
|
||||
|
||||
type ReqSendReceipt struct {
|
||||
ThreadID string `json:"thread_id,omitempty"`
|
||||
}
|
||||
|
||||
// ReqHierarchy contains the parameters for https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv1roomsroomidhierarchy
|
||||
//
|
||||
// As it's a GET method, there is no JSON body, so this is only query parameters.
|
||||
type ReqHierarchy struct {
|
||||
// A pagination token from a previous Hierarchy call.
|
||||
// If specified, max_depth and suggested_only cannot be changed from the first request.
|
||||
From string
|
||||
// Limit for the maximum number of rooms to include per response.
|
||||
// The server will apply a default value if a limit isn't provided.
|
||||
Limit int
|
||||
// Limit for how far to go into the space. When reached, no further child rooms will be returned.
|
||||
// The server will apply a default value if a max depth isn't provided.
|
||||
MaxDepth *int
|
||||
// Flag to indicate whether the server should only consider suggested rooms.
|
||||
// Suggested rooms are annotated in their m.space.child event contents.
|
||||
SuggestedOnly bool
|
||||
}
|
||||
|
||||
func (req *ReqHierarchy) Query() map[string]string {
|
||||
query := map[string]string{}
|
||||
if req == nil {
|
||||
return query
|
||||
}
|
||||
if req.From != "" {
|
||||
query["from"] = req.From
|
||||
}
|
||||
if req.Limit > 0 {
|
||||
query["limit"] = strconv.Itoa(req.Limit)
|
||||
}
|
||||
if req.MaxDepth != nil {
|
||||
query["max_depth"] = strconv.Itoa(*req.MaxDepth)
|
||||
}
|
||||
if req.SuggestedOnly {
|
||||
query["suggested_only"] = "true"
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
type ReqAppservicePing struct {
|
||||
TxnID string `json:"transaction_id,omitempty"`
|
||||
}
|
||||
|
||||
type ReqBeeperMergeRoom struct {
|
||||
NewRoom ReqCreateRoom `json:"create"`
|
||||
Key string `json:"key"`
|
||||
Rooms []id.RoomID `json:"rooms"`
|
||||
User id.UserID `json:"user_id"`
|
||||
}
|
||||
|
||||
type BeeperSplitRoomPart struct {
|
||||
UserID id.UserID `json:"user_id"`
|
||||
Values []string `json:"values"`
|
||||
NewRoom ReqCreateRoom `json:"create"`
|
||||
}
|
||||
|
||||
type ReqBeeperSplitRoom struct {
|
||||
RoomID id.RoomID `json:"-"`
|
||||
|
||||
Key string `json:"key"`
|
||||
Parts []BeeperSplitRoomPart `json:"parts"`
|
||||
}
|
||||
|
||||
type ReqRoomKeysVersionCreate struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
AuthData json.RawMessage `json:"auth_data"`
|
||||
}
|
||||
|
||||
type ReqRoomKeysUpdate struct {
|
||||
Rooms map[id.RoomID]ReqRoomKeysRoomUpdate `json:"rooms"`
|
||||
}
|
||||
|
||||
type ReqRoomKeysRoomUpdate struct {
|
||||
Sessions map[id.SessionID]ReqRoomKeysSessionUpdate `json:"sessions"`
|
||||
}
|
||||
|
||||
type ReqRoomKeysSessionUpdate struct {
|
||||
FirstMessageIndex int `json:"first_message_index"`
|
||||
ForwardedCount int `json:"forwarded_count"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
SessionData json.RawMessage `json:"session_data"`
|
||||
}
|
||||
600
vendor/maunium.net/go/mautrix/responses.go
generated
vendored
Normal file
600
vendor/maunium.net/go/mautrix/responses.go
generated
vendored
Normal file
@@ -0,0 +1,600 @@
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/mautrix/util"
|
||||
"maunium.net/go/mautrix/util/jsontime"
|
||||
)
|
||||
|
||||
// RespWhoami is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3accountwhoami
|
||||
type RespWhoami struct {
|
||||
UserID id.UserID `json:"user_id"`
|
||||
DeviceID id.DeviceID `json:"device_id"`
|
||||
}
|
||||
|
||||
// RespCreateFilter is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3useruseridfilter
|
||||
type RespCreateFilter struct {
|
||||
FilterID string `json:"filter_id"`
|
||||
}
|
||||
|
||||
// RespJoinRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidjoin
|
||||
type RespJoinRoom struct {
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
}
|
||||
|
||||
// RespLeaveRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidleave
|
||||
type RespLeaveRoom struct{}
|
||||
|
||||
// RespForgetRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidforget
|
||||
type RespForgetRoom struct{}
|
||||
|
||||
// RespInviteUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite
|
||||
type RespInviteUser struct{}
|
||||
|
||||
// RespKickUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidkick
|
||||
type RespKickUser struct{}
|
||||
|
||||
// RespBanUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidban
|
||||
type RespBanUser struct{}
|
||||
|
||||
// RespUnbanUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidunban
|
||||
type RespUnbanUser struct{}
|
||||
|
||||
// RespTyping is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidtypinguserid
|
||||
type RespTyping struct{}
|
||||
|
||||
// RespPresence is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3presenceuseridstatus
|
||||
type RespPresence struct {
|
||||
Presence event.Presence `json:"presence"`
|
||||
LastActiveAgo int `json:"last_active_ago"`
|
||||
StatusMsg string `json:"status_msg"`
|
||||
CurrentlyActive bool `json:"currently_active"`
|
||||
}
|
||||
|
||||
// RespJoinedRooms is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3joined_rooms
|
||||
type RespJoinedRooms struct {
|
||||
JoinedRooms []id.RoomID `json:"joined_rooms"`
|
||||
}
|
||||
|
||||
// RespJoinedMembers is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidjoined_members
|
||||
type RespJoinedMembers struct {
|
||||
Joined map[id.UserID]JoinedMember `json:"joined"`
|
||||
}
|
||||
|
||||
type JoinedMember struct {
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
AvatarURL string `json:"avatar_url,omitempty"`
|
||||
}
|
||||
|
||||
// RespMessages is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidmessages
|
||||
type RespMessages struct {
|
||||
Start string `json:"start"`
|
||||
Chunk []*event.Event `json:"chunk"`
|
||||
State []*event.Event `json:"state"`
|
||||
End string `json:"end,omitempty"`
|
||||
}
|
||||
|
||||
// RespContext is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidcontexteventid
|
||||
type RespContext struct {
|
||||
End string `json:"end"`
|
||||
Event *event.Event `json:"event"`
|
||||
EventsAfter []*event.Event `json:"events_after"`
|
||||
EventsBefore []*event.Event `json:"events_before"`
|
||||
Start string `json:"start"`
|
||||
State []*event.Event `json:"state"`
|
||||
}
|
||||
|
||||
// RespSendEvent is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
|
||||
type RespSendEvent struct {
|
||||
EventID id.EventID `json:"event_id"`
|
||||
}
|
||||
|
||||
// RespMediaConfig is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixmediav3config
|
||||
type RespMediaConfig struct {
|
||||
UploadSize int64 `json:"m.upload.size,omitempty"`
|
||||
}
|
||||
|
||||
// RespMediaUpload is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixmediav3upload
|
||||
type RespMediaUpload struct {
|
||||
ContentURI id.ContentURI `json:"content_uri"`
|
||||
}
|
||||
|
||||
// RespCreateMXC is the JSON response for /_matrix/media/v3/create as specified in https://github.com/matrix-org/matrix-spec-proposals/pull/2246
|
||||
type RespCreateMXC struct {
|
||||
ContentURI id.ContentURI `json:"content_uri"`
|
||||
UnusedExpiresAt int `json:"unused_expires_at,omitempty"`
|
||||
UploadURL string `json:"upload_url,omitempty"`
|
||||
}
|
||||
|
||||
// RespPreviewURL is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixmediav3preview_url
|
||||
type RespPreviewURL struct {
|
||||
CanonicalURL string `json:"og:url,omitempty"`
|
||||
Title string `json:"og:title,omitempty"`
|
||||
Type string `json:"og:type,omitempty"`
|
||||
Description string `json:"og:description,omitempty"`
|
||||
|
||||
ImageURL id.ContentURIString `json:"og:image,omitempty"`
|
||||
|
||||
ImageSize int `json:"matrix:image:size,omitempty"`
|
||||
ImageWidth int `json:"og:image:width,omitempty"`
|
||||
ImageHeight int `json:"og:image:height,omitempty"`
|
||||
ImageType string `json:"og:image:type,omitempty"`
|
||||
}
|
||||
|
||||
// RespUserInteractive is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#user-interactive-authentication-api
|
||||
type RespUserInteractive struct {
|
||||
Flows []UIAFlow `json:"flows,omitempty"`
|
||||
Params map[AuthType]interface{} `json:"params,omitempty"`
|
||||
Session string `json:"session,omitempty"`
|
||||
Completed []string `json:"completed,omitempty"`
|
||||
|
||||
ErrCode string `json:"errcode,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type UIAFlow struct {
|
||||
Stages []AuthType `json:"stages,omitempty"`
|
||||
}
|
||||
|
||||
// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName.
|
||||
func (r RespUserInteractive) HasSingleStageFlow(stageName AuthType) bool {
|
||||
for _, f := range r.Flows {
|
||||
if len(f.Stages) == 1 && f.Stages[0] == stageName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RespUserDisplayName is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseriddisplayname
|
||||
type RespUserDisplayName struct {
|
||||
DisplayName string `json:"displayname"`
|
||||
}
|
||||
|
||||
type RespUserProfile struct {
|
||||
DisplayName string `json:"displayname"`
|
||||
AvatarURL id.ContentURI `json:"avatar_url"`
|
||||
}
|
||||
|
||||
// RespRegisterAvailable is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv3registeravailable
|
||||
type RespRegisterAvailable struct {
|
||||
Available bool `json:"available"`
|
||||
}
|
||||
|
||||
// RespRegister is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3register
|
||||
type RespRegister struct {
|
||||
AccessToken string `json:"access_token,omitempty"`
|
||||
DeviceID id.DeviceID `json:"device_id,omitempty"`
|
||||
UserID id.UserID `json:"user_id"`
|
||||
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
ExpiresInMS int64 `json:"expires_in_ms,omitempty"`
|
||||
|
||||
// Deprecated: homeserver should be parsed from the user ID
|
||||
HomeServer string `json:"home_server,omitempty"`
|
||||
}
|
||||
|
||||
type LoginFlow struct {
|
||||
Type AuthType `json:"type"`
|
||||
}
|
||||
|
||||
// RespLoginFlows is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3login
|
||||
type RespLoginFlows struct {
|
||||
Flows []LoginFlow `json:"flows"`
|
||||
}
|
||||
|
||||
func (rlf *RespLoginFlows) FirstFlowOfType(flowTypes ...AuthType) *LoginFlow {
|
||||
for _, flow := range rlf.Flows {
|
||||
for _, flowType := range flowTypes {
|
||||
if flow.Type == flowType {
|
||||
return &flow
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rlf *RespLoginFlows) HasFlow(flowType ...AuthType) bool {
|
||||
return rlf.FirstFlowOfType(flowType...) != nil
|
||||
}
|
||||
|
||||
// RespLogin is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3login
|
||||
type RespLogin struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
DeviceID id.DeviceID `json:"device_id"`
|
||||
UserID id.UserID `json:"user_id"`
|
||||
WellKnown *ClientWellKnown `json:"well_known,omitempty"`
|
||||
}
|
||||
|
||||
// RespLogout is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3logout
|
||||
type RespLogout struct{}
|
||||
|
||||
// RespCreateRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom
|
||||
type RespCreateRoom struct {
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
}
|
||||
|
||||
type RespMembers struct {
|
||||
Chunk []*event.Event `json:"chunk"`
|
||||
}
|
||||
|
||||
type LazyLoadSummary struct {
|
||||
Heroes []id.UserID `json:"m.heroes,omitempty"`
|
||||
JoinedMemberCount *int `json:"m.joined_member_count,omitempty"`
|
||||
InvitedMemberCount *int `json:"m.invited_member_count,omitempty"`
|
||||
}
|
||||
|
||||
type SyncEventsList struct {
|
||||
Events []*event.Event `json:"events,omitempty"`
|
||||
}
|
||||
|
||||
type SyncTimeline struct {
|
||||
SyncEventsList
|
||||
Limited bool `json:"limited,omitempty"`
|
||||
PrevBatch string `json:"prev_batch,omitempty"`
|
||||
}
|
||||
|
||||
// RespSync is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3sync
|
||||
type RespSync struct {
|
||||
NextBatch string `json:"next_batch"`
|
||||
|
||||
AccountData SyncEventsList `json:"account_data"`
|
||||
Presence SyncEventsList `json:"presence"`
|
||||
ToDevice SyncEventsList `json:"to_device"`
|
||||
|
||||
DeviceLists DeviceLists `json:"device_lists"`
|
||||
DeviceOTKCount OTKCount `json:"device_one_time_keys_count"`
|
||||
FallbackKeys []id.KeyAlgorithm `json:"device_unused_fallback_key_types"`
|
||||
|
||||
Rooms RespSyncRooms `json:"rooms"`
|
||||
}
|
||||
|
||||
type RespSyncRooms struct {
|
||||
Leave map[id.RoomID]*SyncLeftRoom `json:"leave,omitempty"`
|
||||
Join map[id.RoomID]*SyncJoinedRoom `json:"join,omitempty"`
|
||||
Invite map[id.RoomID]*SyncInvitedRoom `json:"invite,omitempty"`
|
||||
Knock map[id.RoomID]*SyncKnockedRoom `json:"knock,omitempty"`
|
||||
}
|
||||
|
||||
type marshalableRespSync RespSync
|
||||
|
||||
var syncPathsToDelete = []string{"account_data", "presence", "to_device", "device_lists", "device_one_time_keys_count", "rooms"}
|
||||
|
||||
func (rs *RespSync) MarshalJSON() ([]byte, error) {
|
||||
return util.MarshalAndDeleteEmpty((*marshalableRespSync)(rs), syncPathsToDelete)
|
||||
}
|
||||
|
||||
type DeviceLists struct {
|
||||
Changed []id.UserID `json:"changed,omitempty"`
|
||||
Left []id.UserID `json:"left,omitempty"`
|
||||
}
|
||||
|
||||
type OTKCount struct {
|
||||
Curve25519 int `json:"curve25519,omitempty"`
|
||||
SignedCurve25519 int `json:"signed_curve25519,omitempty"`
|
||||
|
||||
// For appservice OTK counts only: the user ID in question
|
||||
UserID id.UserID `json:"-"`
|
||||
DeviceID id.DeviceID `json:"-"`
|
||||
}
|
||||
|
||||
type SyncLeftRoom struct {
|
||||
Summary LazyLoadSummary `json:"summary"`
|
||||
State SyncEventsList `json:"state"`
|
||||
Timeline SyncTimeline `json:"timeline"`
|
||||
}
|
||||
|
||||
type marshalableSyncLeftRoom SyncLeftRoom
|
||||
|
||||
var syncLeftRoomPathsToDelete = []string{"summary", "state", "timeline"}
|
||||
|
||||
func (slr SyncLeftRoom) MarshalJSON() ([]byte, error) {
|
||||
return util.MarshalAndDeleteEmpty((marshalableSyncLeftRoom)(slr), syncLeftRoomPathsToDelete)
|
||||
}
|
||||
|
||||
type SyncJoinedRoom struct {
|
||||
Summary LazyLoadSummary `json:"summary"`
|
||||
State SyncEventsList `json:"state"`
|
||||
Timeline SyncTimeline `json:"timeline"`
|
||||
Ephemeral SyncEventsList `json:"ephemeral"`
|
||||
AccountData SyncEventsList `json:"account_data"`
|
||||
|
||||
UnreadNotifications *UnreadNotificationCounts `json:"unread_notifications,omitempty"`
|
||||
// https://github.com/matrix-org/matrix-spec-proposals/pull/2654
|
||||
MSC2654UnreadCount *int `json:"org.matrix.msc2654.unread_count,omitempty"`
|
||||
}
|
||||
|
||||
type UnreadNotificationCounts struct {
|
||||
HighlightCount int `json:"highlight_count"`
|
||||
NotificationCount int `json:"notification_count"`
|
||||
}
|
||||
|
||||
type marshalableSyncJoinedRoom SyncJoinedRoom
|
||||
|
||||
var syncJoinedRoomPathsToDelete = []string{"summary", "state", "timeline", "ephemeral", "account_data"}
|
||||
|
||||
func (sjr SyncJoinedRoom) MarshalJSON() ([]byte, error) {
|
||||
return util.MarshalAndDeleteEmpty((marshalableSyncJoinedRoom)(sjr), syncJoinedRoomPathsToDelete)
|
||||
}
|
||||
|
||||
type SyncInvitedRoom struct {
|
||||
Summary LazyLoadSummary `json:"summary"`
|
||||
State SyncEventsList `json:"invite_state"`
|
||||
}
|
||||
|
||||
type marshalableSyncInvitedRoom SyncInvitedRoom
|
||||
|
||||
var syncInvitedRoomPathsToDelete = []string{"summary"}
|
||||
|
||||
func (sir SyncInvitedRoom) MarshalJSON() ([]byte, error) {
|
||||
return util.MarshalAndDeleteEmpty((marshalableSyncInvitedRoom)(sir), syncInvitedRoomPathsToDelete)
|
||||
}
|
||||
|
||||
type SyncKnockedRoom struct {
|
||||
State SyncEventsList `json:"knock_state"`
|
||||
}
|
||||
|
||||
type RespTurnServer struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
TTL int `json:"ttl"`
|
||||
URIs []string `json:"uris"`
|
||||
}
|
||||
|
||||
type RespAliasCreate struct{}
|
||||
type RespAliasDelete struct{}
|
||||
type RespAliasResolve struct {
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
Servers []string `json:"servers"`
|
||||
}
|
||||
type RespAliasList struct {
|
||||
Aliases []id.RoomAlias `json:"aliases"`
|
||||
}
|
||||
|
||||
type RespUploadKeys struct {
|
||||
OneTimeKeyCounts OTKCount `json:"one_time_key_counts"`
|
||||
}
|
||||
|
||||
type RespQueryKeys struct {
|
||||
Failures map[string]interface{} `json:"failures,omitempty"`
|
||||
DeviceKeys map[id.UserID]map[id.DeviceID]DeviceKeys `json:"device_keys"`
|
||||
MasterKeys map[id.UserID]CrossSigningKeys `json:"master_keys"`
|
||||
SelfSigningKeys map[id.UserID]CrossSigningKeys `json:"self_signing_keys"`
|
||||
UserSigningKeys map[id.UserID]CrossSigningKeys `json:"user_signing_keys"`
|
||||
}
|
||||
|
||||
type RespClaimKeys struct {
|
||||
Failures map[string]interface{} `json:"failures,omitempty"`
|
||||
OneTimeKeys map[id.UserID]map[id.DeviceID]map[id.KeyID]OneTimeKey `json:"one_time_keys"`
|
||||
}
|
||||
|
||||
type RespUploadSignatures struct {
|
||||
Failures map[string]interface{} `json:"failures,omitempty"`
|
||||
}
|
||||
|
||||
type RespKeyChanges struct {
|
||||
Changed []id.UserID `json:"changed"`
|
||||
Left []id.UserID `json:"left"`
|
||||
}
|
||||
|
||||
type RespSendToDevice struct{}
|
||||
|
||||
// RespDevicesInfo is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3devices
|
||||
type RespDevicesInfo struct {
|
||||
Devices []RespDeviceInfo `json:"devices"`
|
||||
}
|
||||
|
||||
// RespDeviceInfo is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3devicesdeviceid
|
||||
type RespDeviceInfo struct {
|
||||
DeviceID id.DeviceID `json:"device_id"`
|
||||
DisplayName string `json:"display_name"`
|
||||
LastSeenIP string `json:"last_seen_ip"`
|
||||
LastSeenTS int64 `json:"last_seen_ts"`
|
||||
}
|
||||
|
||||
type RespBatchSend struct {
|
||||
StateEventIDs []id.EventID `json:"state_event_ids"`
|
||||
EventIDs []id.EventID `json:"event_ids"`
|
||||
|
||||
InsertionEventID id.EventID `json:"insertion_event_id"`
|
||||
BatchEventID id.EventID `json:"batch_event_id"`
|
||||
BaseInsertionEventID id.EventID `json:"base_insertion_event_id"`
|
||||
|
||||
NextBatchID id.BatchID `json:"next_batch_id"`
|
||||
}
|
||||
|
||||
// RespCapabilities is the JSON response for https://spec.matrix.org/v1.3/client-server-api/#get_matrixclientv3capabilities
|
||||
type RespCapabilities struct {
|
||||
RoomVersions *CapRoomVersions `json:"m.room_versions,omitempty"`
|
||||
ChangePassword *CapBooleanTrue `json:"m.change_password,omitempty"`
|
||||
SetDisplayname *CapBooleanTrue `json:"m.set_displayname,omitempty"`
|
||||
SetAvatarURL *CapBooleanTrue `json:"m.set_avatar_url,omitempty"`
|
||||
ThreePIDChanges *CapBooleanTrue `json:"m.3pid_changes,omitempty"`
|
||||
|
||||
Custom map[string]interface{} `json:"-"`
|
||||
}
|
||||
|
||||
type serializableRespCapabilities RespCapabilities
|
||||
|
||||
func (rc *RespCapabilities) UnmarshalJSON(data []byte) error {
|
||||
res := gjson.GetBytes(data, "capabilities")
|
||||
if !res.Exists() || !res.IsObject() {
|
||||
return nil
|
||||
}
|
||||
if res.Index > 0 {
|
||||
data = data[res.Index : res.Index+len(res.Raw)]
|
||||
} else {
|
||||
data = []byte(res.Raw)
|
||||
}
|
||||
err := json.Unmarshal(data, (*serializableRespCapabilities)(rc))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(data, &rc.Custom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove non-custom capabilities from the custom map so that they don't get overridden when serializing back
|
||||
for _, field := range reflect.VisibleFields(reflect.TypeOf(rc).Elem()) {
|
||||
jsonTag := strings.Split(field.Tag.Get("json"), ",")[0]
|
||||
if jsonTag != "-" && jsonTag != "" {
|
||||
delete(rc.Custom, jsonTag)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *RespCapabilities) MarshalJSON() ([]byte, error) {
|
||||
marshalableCopy := make(map[string]interface{}, len(rc.Custom))
|
||||
val := reflect.ValueOf(rc).Elem()
|
||||
for _, field := range reflect.VisibleFields(val.Type()) {
|
||||
jsonTag := strings.Split(field.Tag.Get("json"), ",")[0]
|
||||
if jsonTag != "-" && jsonTag != "" {
|
||||
fieldVal := val.FieldByIndex(field.Index)
|
||||
if !fieldVal.IsNil() {
|
||||
marshalableCopy[jsonTag] = fieldVal.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
if rc.Custom != nil {
|
||||
for key, value := range rc.Custom {
|
||||
marshalableCopy[key] = value
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`{"capabilities":`)
|
||||
err := json.NewEncoder(&buf).Encode(marshalableCopy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type CapBoolean struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type CapBooleanTrue CapBoolean
|
||||
|
||||
// IsEnabled returns true if the capability is either enabled explicitly or not specified (nil)
|
||||
func (cb *CapBooleanTrue) IsEnabled() bool {
|
||||
// Default to true when
|
||||
return cb == nil || cb.Enabled
|
||||
}
|
||||
|
||||
type CapBooleanFalse CapBoolean
|
||||
|
||||
// IsEnabled returns true if the capability is enabled explicitly. If it's not specified, this returns false.
|
||||
func (cb *CapBooleanFalse) IsEnabled() bool {
|
||||
return cb != nil && cb.Enabled
|
||||
}
|
||||
|
||||
type CapRoomVersionStability string
|
||||
|
||||
const (
|
||||
CapRoomVersionStable CapRoomVersionStability = "stable"
|
||||
CapRoomVersionUnstable CapRoomVersionStability = "unstable"
|
||||
)
|
||||
|
||||
type CapRoomVersions struct {
|
||||
Default string `json:"default"`
|
||||
Available map[string]CapRoomVersionStability `json:"available"`
|
||||
}
|
||||
|
||||
func (vers *CapRoomVersions) IsStable(version string) bool {
|
||||
if vers == nil || vers.Available == nil {
|
||||
val, err := strconv.Atoi(version)
|
||||
return err == nil && val > 0
|
||||
}
|
||||
return vers.Available[version] == CapRoomVersionStable
|
||||
}
|
||||
|
||||
func (vers *CapRoomVersions) IsAvailable(version string) bool {
|
||||
if vers == nil || vers.Available == nil {
|
||||
return false
|
||||
}
|
||||
_, available := vers.Available[version]
|
||||
return available
|
||||
}
|
||||
|
||||
// RespHierarchy is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv1roomsroomidhierarchy
|
||||
type RespHierarchy struct {
|
||||
NextBatch string `json:"next_batch,omitempty"`
|
||||
Rooms []ChildRoomsChunk `json:"rooms"`
|
||||
}
|
||||
|
||||
type ChildRoomsChunk struct {
|
||||
AvatarURL id.ContentURI `json:"avatar_url,omitempty"`
|
||||
CanonicalAlias id.RoomAlias `json:"canonical_alias,omitempty"`
|
||||
ChildrenState []StrippedStateWithTime `json:"children_state"`
|
||||
GuestCanJoin bool `json:"guest_can_join"`
|
||||
JoinRule event.JoinRule `json:"join_rule,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
NumJoinedMembers int `json:"num_joined_members"`
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
RoomType event.RoomType `json:"room_type"`
|
||||
Topic string `json:"topic,omitempty"`
|
||||
WorldReadble bool `json:"world_readable"`
|
||||
}
|
||||
|
||||
type StrippedStateWithTime struct {
|
||||
event.StrippedState
|
||||
Timestamp jsontime.UnixMilli `json:"origin_server_ts"`
|
||||
}
|
||||
|
||||
type RespAppservicePing struct {
|
||||
DurationMS int64 `json:"duration"`
|
||||
}
|
||||
|
||||
type RespBeeperMergeRoom RespCreateRoom
|
||||
|
||||
type RespBeeperSplitRoom struct {
|
||||
RoomIDs map[string]id.RoomID `json:"room_ids"`
|
||||
}
|
||||
|
||||
type RespTimestampToEvent struct {
|
||||
EventID id.EventID `json:"event_id"`
|
||||
Timestamp jsontime.UnixMilli `json:"origin_server_ts"`
|
||||
}
|
||||
|
||||
type RespRoomKeysVersionCreate struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type RespRoomKeysVersion struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
AuthData json.RawMessage `json:"auth_data"`
|
||||
Count int `json:"count"`
|
||||
ETag string `json:"etag"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type RespRoomKeys struct {
|
||||
Rooms map[id.RoomID]RespRoomKeysRoom `json:"rooms"`
|
||||
}
|
||||
|
||||
type RespRoomKeysRoom struct {
|
||||
Sessions map[id.SessionID]RespRoomKeysSession `json:"sessions"`
|
||||
}
|
||||
|
||||
type RespRoomKeysSession struct {
|
||||
FirstMessageIndex int `json:"first_message_index"`
|
||||
ForwardedCount int `json:"forwarded_count"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
SessionData json.RawMessage `json:"session_data"`
|
||||
}
|
||||
|
||||
type RespRoomKeysUpdate struct {
|
||||
Count int `json:"count"`
|
||||
ETag string `json:"etag"`
|
||||
}
|
||||
54
vendor/maunium.net/go/mautrix/room.go
generated
vendored
Normal file
54
vendor/maunium.net/go/mautrix/room.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type RoomStateMap = map[event.Type]map[string]*event.Event
|
||||
|
||||
// Room represents a single Matrix room.
|
||||
type Room struct {
|
||||
ID id.RoomID
|
||||
State RoomStateMap
|
||||
}
|
||||
|
||||
// UpdateState updates the room's current state with the given Event. This will clobber events based
|
||||
// on the type/state_key combination.
|
||||
func (room Room) UpdateState(evt *event.Event) {
|
||||
_, exists := room.State[evt.Type]
|
||||
if !exists {
|
||||
room.State[evt.Type] = make(map[string]*event.Event)
|
||||
}
|
||||
room.State[evt.Type][*evt.StateKey] = evt
|
||||
}
|
||||
|
||||
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
|
||||
func (room Room) GetStateEvent(eventType event.Type, stateKey string) *event.Event {
|
||||
stateEventMap, _ := room.State[eventType]
|
||||
evt, _ := stateEventMap[stateKey]
|
||||
return evt
|
||||
}
|
||||
|
||||
// GetMembershipState returns the membership state of the given user ID in this room. If there is
|
||||
// no entry for this member, 'leave' is returned for consistency with left users.
|
||||
func (room Room) GetMembershipState(userID id.UserID) event.Membership {
|
||||
state := event.MembershipLeave
|
||||
evt := room.GetStateEvent(event.StateMember, string(userID))
|
||||
if evt != nil {
|
||||
membership, ok := evt.Content.Raw["membership"].(string)
|
||||
if ok {
|
||||
state = event.Membership(membership)
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// NewRoom creates a new Room with the given ID
|
||||
func NewRoom(roomID id.RoomID) *Room {
|
||||
// Init the State map and return a pointer to the Room
|
||||
return &Room{
|
||||
ID: roomID,
|
||||
State: make(RoomStateMap),
|
||||
}
|
||||
}
|
||||
221
vendor/maunium.net/go/mautrix/statestore.go
generated
vendored
Normal file
221
vendor/maunium.net/go/mautrix/statestore.go
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// StateStore is an interface for storing basic room state information.
|
||||
type StateStore interface {
|
||||
IsInRoom(roomID id.RoomID, userID id.UserID) bool
|
||||
IsInvited(roomID id.RoomID, userID id.UserID) bool
|
||||
IsMembership(roomID id.RoomID, userID id.UserID, allowedMemberships ...event.Membership) bool
|
||||
GetMember(roomID id.RoomID, userID id.UserID) *event.MemberEventContent
|
||||
TryGetMember(roomID id.RoomID, userID id.UserID) (*event.MemberEventContent, bool)
|
||||
SetMembership(roomID id.RoomID, userID id.UserID, membership event.Membership)
|
||||
SetMember(roomID id.RoomID, userID id.UserID, member *event.MemberEventContent)
|
||||
|
||||
SetPowerLevels(roomID id.RoomID, levels *event.PowerLevelsEventContent)
|
||||
GetPowerLevels(roomID id.RoomID) *event.PowerLevelsEventContent
|
||||
|
||||
SetEncryptionEvent(roomID id.RoomID, content *event.EncryptionEventContent)
|
||||
IsEncrypted(roomID id.RoomID) bool
|
||||
}
|
||||
|
||||
func UpdateStateStore(store StateStore, evt *event.Event) {
|
||||
if store == nil || evt == nil || evt.StateKey == nil {
|
||||
return
|
||||
}
|
||||
// We only care about events without a state key (power levels, encryption) or member events with state key
|
||||
if evt.Type != event.StateMember && evt.GetStateKey() != "" {
|
||||
return
|
||||
}
|
||||
switch content := evt.Content.Parsed.(type) {
|
||||
case *event.MemberEventContent:
|
||||
store.SetMember(evt.RoomID, id.UserID(evt.GetStateKey()), content)
|
||||
case *event.PowerLevelsEventContent:
|
||||
store.SetPowerLevels(evt.RoomID, content)
|
||||
case *event.EncryptionEventContent:
|
||||
store.SetEncryptionEvent(evt.RoomID, content)
|
||||
}
|
||||
}
|
||||
|
||||
// StateStoreSyncHandler can be added as an event handler in the syncer to update the state store automatically.
|
||||
//
|
||||
// client.Syncer.(mautrix.ExtensibleSyncer).OnEvent(client.StateStoreSyncHandler)
|
||||
//
|
||||
// DefaultSyncer.ParseEventContent must also be true for this to work (which it is by default).
|
||||
func (cli *Client) StateStoreSyncHandler(_ EventSource, evt *event.Event) {
|
||||
UpdateStateStore(cli.StateStore, evt)
|
||||
}
|
||||
|
||||
type MemoryStateStore struct {
|
||||
Registrations map[id.UserID]bool `json:"registrations"`
|
||||
Members map[id.RoomID]map[id.UserID]*event.MemberEventContent `json:"memberships"`
|
||||
PowerLevels map[id.RoomID]*event.PowerLevelsEventContent `json:"power_levels"`
|
||||
Encryption map[id.RoomID]*event.EncryptionEventContent `json:"encryption"`
|
||||
|
||||
registrationsLock sync.RWMutex
|
||||
membersLock sync.RWMutex
|
||||
powerLevelsLock sync.RWMutex
|
||||
encryptionLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewMemoryStateStore() StateStore {
|
||||
return &MemoryStateStore{
|
||||
Registrations: make(map[id.UserID]bool),
|
||||
Members: make(map[id.RoomID]map[id.UserID]*event.MemberEventContent),
|
||||
PowerLevels: make(map[id.RoomID]*event.PowerLevelsEventContent),
|
||||
}
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) IsRegistered(userID id.UserID) bool {
|
||||
store.registrationsLock.RLock()
|
||||
defer store.registrationsLock.RUnlock()
|
||||
registered, ok := store.Registrations[userID]
|
||||
return ok && registered
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) MarkRegistered(userID id.UserID) {
|
||||
store.registrationsLock.Lock()
|
||||
defer store.registrationsLock.Unlock()
|
||||
store.Registrations[userID] = true
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) GetRoomMembers(roomID id.RoomID) map[id.UserID]*event.MemberEventContent {
|
||||
store.membersLock.RLock()
|
||||
members, ok := store.Members[roomID]
|
||||
store.membersLock.RUnlock()
|
||||
if !ok {
|
||||
members = make(map[id.UserID]*event.MemberEventContent)
|
||||
store.membersLock.Lock()
|
||||
store.Members[roomID] = members
|
||||
store.membersLock.Unlock()
|
||||
}
|
||||
return members
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) GetMembership(roomID id.RoomID, userID id.UserID) event.Membership {
|
||||
return store.GetMember(roomID, userID).Membership
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) GetMember(roomID id.RoomID, userID id.UserID) *event.MemberEventContent {
|
||||
member, ok := store.TryGetMember(roomID, userID)
|
||||
if !ok {
|
||||
member = &event.MemberEventContent{Membership: event.MembershipLeave}
|
||||
}
|
||||
return member
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) TryGetMember(roomID id.RoomID, userID id.UserID) (member *event.MemberEventContent, ok bool) {
|
||||
store.membersLock.RLock()
|
||||
defer store.membersLock.RUnlock()
|
||||
members, membersOk := store.Members[roomID]
|
||||
if !membersOk {
|
||||
return
|
||||
}
|
||||
member, ok = members[userID]
|
||||
return
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) IsInRoom(roomID id.RoomID, userID id.UserID) bool {
|
||||
return store.IsMembership(roomID, userID, "join")
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) IsInvited(roomID id.RoomID, userID id.UserID) bool {
|
||||
return store.IsMembership(roomID, userID, "join", "invite")
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) IsMembership(roomID id.RoomID, userID id.UserID, allowedMemberships ...event.Membership) bool {
|
||||
membership := store.GetMembership(roomID, userID)
|
||||
for _, allowedMembership := range allowedMemberships {
|
||||
if allowedMembership == membership {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) SetMembership(roomID id.RoomID, userID id.UserID, membership event.Membership) {
|
||||
store.membersLock.Lock()
|
||||
members, ok := store.Members[roomID]
|
||||
if !ok {
|
||||
members = map[id.UserID]*event.MemberEventContent{
|
||||
userID: {Membership: membership},
|
||||
}
|
||||
} else {
|
||||
member, ok := members[userID]
|
||||
if !ok {
|
||||
members[userID] = &event.MemberEventContent{Membership: membership}
|
||||
} else {
|
||||
member.Membership = membership
|
||||
members[userID] = member
|
||||
}
|
||||
}
|
||||
store.Members[roomID] = members
|
||||
store.membersLock.Unlock()
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) SetMember(roomID id.RoomID, userID id.UserID, member *event.MemberEventContent) {
|
||||
store.membersLock.Lock()
|
||||
members, ok := store.Members[roomID]
|
||||
if !ok {
|
||||
members = map[id.UserID]*event.MemberEventContent{
|
||||
userID: member,
|
||||
}
|
||||
} else {
|
||||
members[userID] = member
|
||||
}
|
||||
store.Members[roomID] = members
|
||||
store.membersLock.Unlock()
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) SetPowerLevels(roomID id.RoomID, levels *event.PowerLevelsEventContent) {
|
||||
store.powerLevelsLock.Lock()
|
||||
store.PowerLevels[roomID] = levels
|
||||
store.powerLevelsLock.Unlock()
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) GetPowerLevels(roomID id.RoomID) (levels *event.PowerLevelsEventContent) {
|
||||
store.powerLevelsLock.RLock()
|
||||
levels = store.PowerLevels[roomID]
|
||||
store.powerLevelsLock.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) GetPowerLevel(roomID id.RoomID, userID id.UserID) int {
|
||||
return store.GetPowerLevels(roomID).GetUserLevel(userID)
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) GetPowerLevelRequirement(roomID id.RoomID, eventType event.Type) int {
|
||||
return store.GetPowerLevels(roomID).GetEventLevel(eventType)
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) HasPowerLevel(roomID id.RoomID, userID id.UserID, eventType event.Type) bool {
|
||||
return store.GetPowerLevel(roomID, userID) >= store.GetPowerLevelRequirement(roomID, eventType)
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) SetEncryptionEvent(roomID id.RoomID, content *event.EncryptionEventContent) {
|
||||
store.encryptionLock.Lock()
|
||||
store.Encryption[roomID] = content
|
||||
store.encryptionLock.Unlock()
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) GetEncryptionEvent(roomID id.RoomID) *event.EncryptionEventContent {
|
||||
store.encryptionLock.RLock()
|
||||
defer store.encryptionLock.RUnlock()
|
||||
return store.Encryption[roomID]
|
||||
}
|
||||
|
||||
func (store *MemoryStateStore) IsEncrypted(roomID id.RoomID) bool {
|
||||
cfg := store.GetEncryptionEvent(roomID)
|
||||
return cfg != nil && cfg.Algorithm == id.AlgorithmMegolmV1
|
||||
}
|
||||
310
vendor/maunium.net/go/mautrix/sync.go
generated
vendored
Normal file
310
vendor/maunium.net/go/mautrix/sync.go
generated
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// EventSource represents the part of the sync response that an event came from.
|
||||
type EventSource int
|
||||
|
||||
const (
|
||||
EventSourcePresence EventSource = 1 << iota
|
||||
EventSourceJoin
|
||||
EventSourceInvite
|
||||
EventSourceLeave
|
||||
EventSourceAccountData
|
||||
EventSourceTimeline
|
||||
EventSourceState
|
||||
EventSourceEphemeral
|
||||
EventSourceToDevice
|
||||
EventSourceDecrypted
|
||||
)
|
||||
|
||||
const primaryTypes = EventSourcePresence | EventSourceAccountData | EventSourceToDevice | EventSourceTimeline | EventSourceState
|
||||
const roomSections = EventSourceJoin | EventSourceInvite | EventSourceLeave
|
||||
const roomableTypes = EventSourceAccountData | EventSourceTimeline | EventSourceState
|
||||
const encryptableTypes = roomableTypes | EventSourceToDevice
|
||||
|
||||
func (es EventSource) String() string {
|
||||
var typeName string
|
||||
switch es & primaryTypes {
|
||||
case EventSourcePresence:
|
||||
typeName = "presence"
|
||||
case EventSourceAccountData:
|
||||
typeName = "account data"
|
||||
case EventSourceToDevice:
|
||||
typeName = "to-device"
|
||||
case EventSourceTimeline:
|
||||
typeName = "timeline"
|
||||
case EventSourceState:
|
||||
typeName = "state"
|
||||
default:
|
||||
return fmt.Sprintf("unknown (%d)", es)
|
||||
}
|
||||
if es&roomableTypes != 0 {
|
||||
switch es & roomSections {
|
||||
case EventSourceJoin:
|
||||
typeName = "joined room " + typeName
|
||||
case EventSourceInvite:
|
||||
typeName = "invited room " + typeName
|
||||
case EventSourceLeave:
|
||||
typeName = "left room " + typeName
|
||||
default:
|
||||
return fmt.Sprintf("unknown (%d)", es)
|
||||
}
|
||||
es &^= roomableTypes
|
||||
}
|
||||
if es&encryptableTypes != 0 && es&EventSourceDecrypted != 0 {
|
||||
typeName += " (decrypted)"
|
||||
es &^= EventSourceDecrypted
|
||||
}
|
||||
es &^= primaryTypes
|
||||
if es != 0 {
|
||||
return fmt.Sprintf("unknown (%d)", es)
|
||||
}
|
||||
return typeName
|
||||
}
|
||||
|
||||
// EventHandler handles a single event from a sync response.
|
||||
type EventHandler func(source EventSource, evt *event.Event)
|
||||
|
||||
// SyncHandler handles a whole sync response. If the return value is false, handling will be stopped completely.
|
||||
type SyncHandler func(resp *RespSync, since string) bool
|
||||
|
||||
// Syncer is an interface that must be satisfied in order to do /sync requests on a client.
|
||||
type Syncer interface {
|
||||
// ProcessResponse processes the /sync response. The since parameter is the since= value that was used to produce the response.
|
||||
// This is useful for detecting the very first sync (since=""). If an error is return, Syncing will be stopped permanently.
|
||||
ProcessResponse(resp *RespSync, since string) error
|
||||
// OnFailedSync returns either the time to wait before retrying or an error to stop syncing permanently.
|
||||
OnFailedSync(res *RespSync, err error) (time.Duration, error)
|
||||
// GetFilterJSON for the given user ID. NOT the filter ID.
|
||||
GetFilterJSON(userID id.UserID) *Filter
|
||||
}
|
||||
|
||||
type ExtensibleSyncer interface {
|
||||
OnSync(callback SyncHandler)
|
||||
OnEvent(callback EventHandler)
|
||||
OnEventType(eventType event.Type, callback EventHandler)
|
||||
}
|
||||
|
||||
type DispatchableSyncer interface {
|
||||
Dispatch(source EventSource, evt *event.Event)
|
||||
}
|
||||
|
||||
// DefaultSyncer is the default syncing implementation. You can either write your own syncer, or selectively
|
||||
// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
|
||||
// pattern to notify callers about incoming events. See DefaultSyncer.OnEventType for more information.
|
||||
type DefaultSyncer struct {
|
||||
// syncListeners want the whole sync response, e.g. the crypto machine
|
||||
syncListeners []SyncHandler
|
||||
// globalListeners want all events
|
||||
globalListeners []EventHandler
|
||||
// listeners want a specific event type
|
||||
listeners map[event.Type][]EventHandler
|
||||
// ParseEventContent determines whether or not event content should be parsed before passing to handlers.
|
||||
ParseEventContent bool
|
||||
// ParseErrorHandler is called when event.Content.ParseRaw returns an error.
|
||||
// If it returns false, the event will not be forwarded to listeners.
|
||||
ParseErrorHandler func(evt *event.Event, err error) bool
|
||||
// FilterJSON is used when the client starts syncing and doesn't get an existing filter ID from SyncStore's LoadFilterID.
|
||||
FilterJSON *Filter
|
||||
}
|
||||
|
||||
var _ Syncer = (*DefaultSyncer)(nil)
|
||||
var _ ExtensibleSyncer = (*DefaultSyncer)(nil)
|
||||
|
||||
// NewDefaultSyncer returns an instantiated DefaultSyncer
|
||||
func NewDefaultSyncer() *DefaultSyncer {
|
||||
return &DefaultSyncer{
|
||||
listeners: make(map[event.Type][]EventHandler),
|
||||
syncListeners: []SyncHandler{},
|
||||
globalListeners: []EventHandler{},
|
||||
ParseEventContent: true,
|
||||
ParseErrorHandler: func(evt *event.Event, err error) bool {
|
||||
return false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of
|
||||
// unrepeating events. Returns a fatal error if a listener panics.
|
||||
func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("ProcessResponse panicked! since=%s panic=%s\n%s", since, r, debug.Stack())
|
||||
}
|
||||
}()
|
||||
|
||||
for _, listener := range s.syncListeners {
|
||||
if !listener(res, since) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.processSyncEvents("", res.ToDevice.Events, EventSourceToDevice)
|
||||
s.processSyncEvents("", res.Presence.Events, EventSourcePresence)
|
||||
s.processSyncEvents("", res.AccountData.Events, EventSourceAccountData)
|
||||
|
||||
for roomID, roomData := range res.Rooms.Join {
|
||||
s.processSyncEvents(roomID, roomData.State.Events, EventSourceJoin|EventSourceState)
|
||||
s.processSyncEvents(roomID, roomData.Timeline.Events, EventSourceJoin|EventSourceTimeline)
|
||||
s.processSyncEvents(roomID, roomData.Ephemeral.Events, EventSourceJoin|EventSourceEphemeral)
|
||||
s.processSyncEvents(roomID, roomData.AccountData.Events, EventSourceJoin|EventSourceAccountData)
|
||||
}
|
||||
for roomID, roomData := range res.Rooms.Invite {
|
||||
s.processSyncEvents(roomID, roomData.State.Events, EventSourceInvite|EventSourceState)
|
||||
}
|
||||
for roomID, roomData := range res.Rooms.Leave {
|
||||
s.processSyncEvents(roomID, roomData.State.Events, EventSourceLeave|EventSourceState)
|
||||
s.processSyncEvents(roomID, roomData.Timeline.Events, EventSourceLeave|EventSourceTimeline)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *DefaultSyncer) processSyncEvents(roomID id.RoomID, events []*event.Event, source EventSource) {
|
||||
for _, evt := range events {
|
||||
s.processSyncEvent(roomID, evt, source)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DefaultSyncer) processSyncEvent(roomID id.RoomID, evt *event.Event, source EventSource) {
|
||||
evt.RoomID = roomID
|
||||
|
||||
// Ensure the type class is correct. It's safe to mutate the class since the event type is not a pointer.
|
||||
// Listeners are keyed by type structs, which means only the correct class will pass.
|
||||
switch {
|
||||
case evt.StateKey != nil:
|
||||
evt.Type.Class = event.StateEventType
|
||||
case source == EventSourcePresence, source&EventSourceEphemeral != 0:
|
||||
evt.Type.Class = event.EphemeralEventType
|
||||
case source&EventSourceAccountData != 0:
|
||||
evt.Type.Class = event.AccountDataEventType
|
||||
case source == EventSourceToDevice:
|
||||
evt.Type.Class = event.ToDeviceEventType
|
||||
default:
|
||||
evt.Type.Class = event.MessageEventType
|
||||
}
|
||||
|
||||
if s.ParseEventContent {
|
||||
err := evt.Content.ParseRaw(evt.Type)
|
||||
if err != nil && !s.ParseErrorHandler(evt, err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.Dispatch(source, evt)
|
||||
}
|
||||
|
||||
func (s *DefaultSyncer) Dispatch(source EventSource, evt *event.Event) {
|
||||
for _, fn := range s.globalListeners {
|
||||
fn(source, evt)
|
||||
}
|
||||
listeners, exists := s.listeners[evt.Type]
|
||||
if exists {
|
||||
for _, fn := range listeners {
|
||||
fn(source, evt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OnEventType allows callers to be notified when there are new events for the given event type.
|
||||
// There are no duplicate checks.
|
||||
func (s *DefaultSyncer) OnEventType(eventType event.Type, callback EventHandler) {
|
||||
_, exists := s.listeners[eventType]
|
||||
if !exists {
|
||||
s.listeners[eventType] = []EventHandler{}
|
||||
}
|
||||
s.listeners[eventType] = append(s.listeners[eventType], callback)
|
||||
}
|
||||
|
||||
func (s *DefaultSyncer) OnSync(callback SyncHandler) {
|
||||
s.syncListeners = append(s.syncListeners, callback)
|
||||
}
|
||||
|
||||
func (s *DefaultSyncer) OnEvent(callback EventHandler) {
|
||||
s.globalListeners = append(s.globalListeners, callback)
|
||||
}
|
||||
|
||||
// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error.
|
||||
func (s *DefaultSyncer) OnFailedSync(res *RespSync, err error) (time.Duration, error) {
|
||||
if errors.Is(err, MUnknownToken) {
|
||||
return 0, err
|
||||
}
|
||||
return 10 * time.Second, nil
|
||||
}
|
||||
|
||||
var defaultFilter = Filter{
|
||||
Room: RoomFilter{
|
||||
Timeline: FilterPart{
|
||||
Limit: 50,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// GetFilterJSON returns a filter with a timeline limit of 50.
|
||||
func (s *DefaultSyncer) GetFilterJSON(userID id.UserID) *Filter {
|
||||
if s.FilterJSON == nil {
|
||||
defaultFilterCopy := defaultFilter
|
||||
s.FilterJSON = &defaultFilterCopy
|
||||
}
|
||||
return s.FilterJSON
|
||||
}
|
||||
|
||||
// OldEventIgnorer is an utility struct for bots to ignore events from before the bot joined the room.
|
||||
//
|
||||
// Create a struct and call Register with your DefaultSyncer to register the sync handler, e.g.:
|
||||
//
|
||||
// (&OldEventIgnorer{UserID: cli.UserID}).Register(cli.Syncer.(mautrix.ExtensibleSyncer))
|
||||
type OldEventIgnorer struct {
|
||||
UserID id.UserID
|
||||
}
|
||||
|
||||
func (oei *OldEventIgnorer) Register(syncer ExtensibleSyncer) {
|
||||
syncer.OnSync(oei.DontProcessOldEvents)
|
||||
}
|
||||
|
||||
// DontProcessOldEvents returns true if a sync response should be processed. May modify the response to remove
|
||||
// stuff that shouldn't be processed.
|
||||
func (oei *OldEventIgnorer) DontProcessOldEvents(resp *RespSync, since string) bool {
|
||||
if since == "" {
|
||||
return false
|
||||
}
|
||||
// This is a horrible hack because /sync will return the most recent messages for a room
|
||||
// as soon as you /join it. We do NOT want to process those events in that particular room
|
||||
// because they may have already been processed (if you toggle the bot in/out of the room).
|
||||
//
|
||||
// Work around this by inspecting each room's timeline and seeing if an m.room.member event for us
|
||||
// exists and is "join" and then discard processing that room entirely if so.
|
||||
// TODO: We probably want to process messages from after the last join event in the timeline.
|
||||
for roomID, roomData := range resp.Rooms.Join {
|
||||
for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
|
||||
evt := roomData.Timeline.Events[i]
|
||||
if evt.Type == event.StateMember && evt.GetStateKey() == string(oei.UserID) {
|
||||
membership, _ := evt.Content.Raw["membership"].(string)
|
||||
if membership == "join" {
|
||||
_, ok := resp.Rooms.Join[roomID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
delete(resp.Rooms.Join, roomID) // don't re-process messages
|
||||
delete(resp.Rooms.Invite, roomID) // don't re-process invites
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
149
vendor/maunium.net/go/mautrix/syncstore.go
generated
vendored
Normal file
149
vendor/maunium.net/go/mautrix/syncstore.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// SyncStore is an interface which must be satisfied to store client data.
|
||||
//
|
||||
// You can either write a struct which persists this data to disk, or you can use the
|
||||
// provided "MemorySyncStore" which just keeps data around in-memory which is lost on
|
||||
// restarts.
|
||||
type SyncStore interface {
|
||||
SaveFilterID(userID id.UserID, filterID string)
|
||||
LoadFilterID(userID id.UserID) string
|
||||
SaveNextBatch(userID id.UserID, nextBatchToken string)
|
||||
LoadNextBatch(userID id.UserID) string
|
||||
}
|
||||
|
||||
// Deprecated: renamed to SyncStore
|
||||
type Storer = SyncStore
|
||||
|
||||
// MemorySyncStore implements the Storer interface.
|
||||
//
|
||||
// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs
|
||||
// or next batch tokens on any goroutine other than the syncing goroutine: the one
|
||||
// which called Client.Sync().
|
||||
type MemorySyncStore struct {
|
||||
Filters map[id.UserID]string
|
||||
NextBatch map[id.UserID]string
|
||||
}
|
||||
|
||||
// SaveFilterID to memory.
|
||||
func (s *MemorySyncStore) SaveFilterID(userID id.UserID, filterID string) {
|
||||
s.Filters[userID] = filterID
|
||||
}
|
||||
|
||||
// LoadFilterID from memory.
|
||||
func (s *MemorySyncStore) LoadFilterID(userID id.UserID) string {
|
||||
return s.Filters[userID]
|
||||
}
|
||||
|
||||
// SaveNextBatch to memory.
|
||||
func (s *MemorySyncStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
|
||||
s.NextBatch[userID] = nextBatchToken
|
||||
}
|
||||
|
||||
// LoadNextBatch from memory.
|
||||
func (s *MemorySyncStore) LoadNextBatch(userID id.UserID) string {
|
||||
return s.NextBatch[userID]
|
||||
}
|
||||
|
||||
// NewMemorySyncStore constructs a new MemorySyncStore.
|
||||
func NewMemorySyncStore() *MemorySyncStore {
|
||||
return &MemorySyncStore{
|
||||
Filters: make(map[id.UserID]string),
|
||||
NextBatch: make(map[id.UserID]string),
|
||||
}
|
||||
}
|
||||
|
||||
// AccountDataStore uses account data to store the next batch token, and stores the filter ID in memory
|
||||
// (as filters can be safely recreated every startup).
|
||||
type AccountDataStore struct {
|
||||
FilterID string
|
||||
EventType string
|
||||
client *Client
|
||||
}
|
||||
|
||||
type accountData struct {
|
||||
NextBatch string `json:"next_batch"`
|
||||
}
|
||||
|
||||
func (s *AccountDataStore) SaveFilterID(userID id.UserID, filterID string) {
|
||||
if userID.String() != s.client.UserID.String() {
|
||||
panic("AccountDataStore must only be used with a single account")
|
||||
}
|
||||
s.FilterID = filterID
|
||||
}
|
||||
|
||||
func (s *AccountDataStore) LoadFilterID(userID id.UserID) string {
|
||||
if userID.String() != s.client.UserID.String() {
|
||||
panic("AccountDataStore must only be used with a single account")
|
||||
}
|
||||
return s.FilterID
|
||||
}
|
||||
|
||||
func (s *AccountDataStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
|
||||
if userID.String() != s.client.UserID.String() {
|
||||
panic("AccountDataStore must only be used with a single account")
|
||||
}
|
||||
|
||||
data := accountData{
|
||||
NextBatch: nextBatchToken,
|
||||
}
|
||||
|
||||
err := s.client.SetAccountData(s.EventType, data)
|
||||
if err != nil {
|
||||
s.client.Log.Warn().Err(err).Msg("Failed to save next batch token to account data")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AccountDataStore) LoadNextBatch(userID id.UserID) string {
|
||||
if userID.String() != s.client.UserID.String() {
|
||||
panic("AccountDataStore must only be used with a single account")
|
||||
}
|
||||
|
||||
data := &accountData{}
|
||||
|
||||
err := s.client.GetAccountData(s.EventType, data)
|
||||
if err != nil {
|
||||
s.client.Log.Warn().Err(err).Msg("Failed to load next batch token from account data")
|
||||
return ""
|
||||
}
|
||||
|
||||
return data.NextBatch
|
||||
}
|
||||
|
||||
// NewAccountDataStore returns a new AccountDataStore, which stores
|
||||
// the next_batch token as a custom event in account data in the
|
||||
// homeserver.
|
||||
//
|
||||
// AccountDataStore is only appropriate for bots, not appservices.
|
||||
//
|
||||
// The event type should be a reversed DNS name like tld.domain.sub.internal and
|
||||
// must be unique for a client. The data stored in it is considered internal
|
||||
// and must not be modified through outside means. You should also add a filter
|
||||
// for account data changes of this event type, to avoid ending up in a sync
|
||||
// loop:
|
||||
//
|
||||
// filter := mautrix.Filter{
|
||||
// AccountData: mautrix.FilterPart{
|
||||
// Limit: 20,
|
||||
// NotTypes: []event.Type{
|
||||
// event.NewEventType(eventType),
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// // If you use a custom Syncer, set the filter there, not like this
|
||||
// client.Syncer.(*mautrix.DefaultSyncer).FilterJSON = &filter
|
||||
// client.Store = mautrix.NewAccountDataStore("com.example.mybot.store", client)
|
||||
// go func() {
|
||||
// err := client.Sync()
|
||||
// // don't forget to check err
|
||||
// }()
|
||||
func NewAccountDataStore(eventType string, client *Client) *AccountDataStore {
|
||||
return &AccountDataStore{
|
||||
EventType: eventType,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
106
vendor/maunium.net/go/mautrix/url.go
generated
vendored
Normal file
106
vendor/maunium.net/go/mautrix/url.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ParseAndNormalizeBaseURL(homeserverURL string) (*url.URL, error) {
|
||||
hsURL, err := url.Parse(homeserverURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hsURL.Scheme == "" {
|
||||
hsURL.Scheme = "https"
|
||||
fixedURL := hsURL.String()
|
||||
hsURL, err = url.Parse(fixedURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse fixed URL '%s': %v", fixedURL, err)
|
||||
}
|
||||
}
|
||||
hsURL.RawPath = hsURL.EscapedPath()
|
||||
return hsURL, nil
|
||||
}
|
||||
|
||||
// BuildURL builds a URL with the given path parts
|
||||
func BuildURL(baseURL *url.URL, path ...interface{}) *url.URL {
|
||||
createdURL := *baseURL
|
||||
rawParts := make([]string, len(path)+1)
|
||||
rawParts[0] = strings.TrimSuffix(createdURL.RawPath, "/")
|
||||
parts := make([]string, len(path)+1)
|
||||
parts[0] = strings.TrimSuffix(createdURL.Path, "/")
|
||||
for i, part := range path {
|
||||
switch casted := part.(type) {
|
||||
case string:
|
||||
parts[i+1] = casted
|
||||
case int:
|
||||
parts[i+1] = strconv.Itoa(casted)
|
||||
case fmt.Stringer:
|
||||
parts[i+1] = casted.String()
|
||||
default:
|
||||
parts[i+1] = fmt.Sprint(casted)
|
||||
}
|
||||
rawParts[i+1] = url.PathEscape(parts[i+1])
|
||||
}
|
||||
createdURL.Path = strings.Join(parts, "/")
|
||||
createdURL.RawPath = strings.Join(rawParts, "/")
|
||||
return &createdURL
|
||||
}
|
||||
|
||||
// BuildURL builds a URL with the Client's homeserver and appservice user ID set already.
|
||||
func (cli *Client) BuildURL(urlPath PrefixableURLPath) string {
|
||||
return cli.BuildURLWithQuery(urlPath, nil)
|
||||
}
|
||||
|
||||
// BuildClientURL builds a URL with the Client's homeserver and appservice user ID set already.
|
||||
// This method also automatically prepends the client API prefix (/_matrix/client).
|
||||
func (cli *Client) BuildClientURL(urlPath ...interface{}) string {
|
||||
return cli.BuildURLWithQuery(ClientURLPath(urlPath), nil)
|
||||
}
|
||||
|
||||
type PrefixableURLPath interface {
|
||||
FullPath() []interface{}
|
||||
}
|
||||
|
||||
type BaseURLPath []interface{}
|
||||
|
||||
func (bup BaseURLPath) FullPath() []interface{} {
|
||||
return bup
|
||||
}
|
||||
|
||||
type ClientURLPath []interface{}
|
||||
|
||||
func (cup ClientURLPath) FullPath() []interface{} {
|
||||
return append([]interface{}{"_matrix", "client"}, []interface{}(cup)...)
|
||||
}
|
||||
|
||||
type MediaURLPath []interface{}
|
||||
|
||||
func (mup MediaURLPath) FullPath() []interface{} {
|
||||
return append([]interface{}{"_matrix", "media"}, []interface{}(mup)...)
|
||||
}
|
||||
|
||||
// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver
|
||||
// and appservice user ID set already.
|
||||
func (cli *Client) BuildURLWithQuery(urlPath PrefixableURLPath, urlQuery map[string]string) string {
|
||||
hsURL := *BuildURL(cli.HomeserverURL, urlPath.FullPath()...)
|
||||
query := hsURL.Query()
|
||||
if cli.SetAppServiceUserID {
|
||||
query.Set("user_id", string(cli.UserID))
|
||||
}
|
||||
if urlQuery != nil {
|
||||
for k, v := range urlQuery {
|
||||
query.Set(k, v)
|
||||
}
|
||||
}
|
||||
hsURL.RawQuery = query.Encode()
|
||||
return hsURL.String()
|
||||
}
|
||||
9
vendor/maunium.net/go/mautrix/util/base58/README.md
generated
vendored
Normal file
9
vendor/maunium.net/go/mautrix/util/base58/README.md
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
base58
|
||||
==========
|
||||
|
||||
This is a copy of <https://github.com/btcsuite/btcd/tree/master/btcutil/base58>.
|
||||
|
||||
## License
|
||||
|
||||
Package base58 is licensed under the [copyfree](http://copyfree.org) ISC
|
||||
License.
|
||||
49
vendor/maunium.net/go/mautrix/util/base58/alphabet.go
generated
vendored
Normal file
49
vendor/maunium.net/go/mautrix/util/base58/alphabet.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// AUTOGENERATED by genalphabet.go; do not edit.
|
||||
|
||||
package base58
|
||||
|
||||
const (
|
||||
// alphabet is the modified base58 alphabet used by Bitcoin.
|
||||
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
|
||||
alphabetIdx0 = '1'
|
||||
)
|
||||
|
||||
var b58 = [256]byte{
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 255, 255, 255, 255, 255, 255,
|
||||
255, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 255, 17, 18, 19, 20, 21, 255,
|
||||
22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 31, 32, 255, 255, 255, 255, 255,
|
||||
255, 33, 34, 35, 36, 37, 38, 39,
|
||||
40, 41, 42, 43, 255, 44, 45, 46,
|
||||
47, 48, 49, 50, 51, 52, 53, 54,
|
||||
55, 56, 57, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
}
|
||||
138
vendor/maunium.net/go/mautrix/util/base58/base58.go
generated
vendored
Normal file
138
vendor/maunium.net/go/mautrix/util/base58/base58.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright (c) 2013-2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package base58
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
//go:generate go run genalphabet.go
|
||||
|
||||
var bigRadix = [...]*big.Int{
|
||||
big.NewInt(0),
|
||||
big.NewInt(58),
|
||||
big.NewInt(58 * 58),
|
||||
big.NewInt(58 * 58 * 58),
|
||||
big.NewInt(58 * 58 * 58 * 58),
|
||||
big.NewInt(58 * 58 * 58 * 58 * 58),
|
||||
big.NewInt(58 * 58 * 58 * 58 * 58 * 58),
|
||||
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58),
|
||||
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
|
||||
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
|
||||
bigRadix10,
|
||||
}
|
||||
|
||||
var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10
|
||||
|
||||
// Decode decodes a modified base58 string to a byte slice.
|
||||
func Decode(b string) []byte {
|
||||
answer := big.NewInt(0)
|
||||
scratch := new(big.Int)
|
||||
|
||||
// Calculating with big.Int is slow for each iteration.
|
||||
// x += b58[b[i]] * j
|
||||
// j *= 58
|
||||
//
|
||||
// Instead we can try to do as much calculations on int64.
|
||||
// We can represent a 10 digit base58 number using an int64.
|
||||
//
|
||||
// Hence we'll try to convert 10, base58 digits at a time.
|
||||
// The rough idea is to calculate `t`, such that:
|
||||
//
|
||||
// t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0
|
||||
// x *= 58^10
|
||||
// x += t
|
||||
//
|
||||
// Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10.
|
||||
// In that case we'll use the bigRadix[n] lookup for the appropriate power.
|
||||
for t := b; len(t) > 0; {
|
||||
n := len(t)
|
||||
if n > 10 {
|
||||
n = 10
|
||||
}
|
||||
|
||||
total := uint64(0)
|
||||
for _, v := range t[:n] {
|
||||
tmp := b58[v]
|
||||
if tmp == 255 {
|
||||
return []byte("")
|
||||
}
|
||||
total = total*58 + uint64(tmp)
|
||||
}
|
||||
|
||||
answer.Mul(answer, bigRadix[n])
|
||||
scratch.SetUint64(total)
|
||||
answer.Add(answer, scratch)
|
||||
|
||||
t = t[n:]
|
||||
}
|
||||
|
||||
tmpval := answer.Bytes()
|
||||
|
||||
var numZeros int
|
||||
for numZeros = 0; numZeros < len(b); numZeros++ {
|
||||
if b[numZeros] != alphabetIdx0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
flen := numZeros + len(tmpval)
|
||||
val := make([]byte, flen)
|
||||
copy(val[numZeros:], tmpval)
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
// Encode encodes a byte slice to a modified base58 string.
|
||||
func Encode(b []byte) string {
|
||||
x := new(big.Int)
|
||||
x.SetBytes(b)
|
||||
|
||||
// maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58)
|
||||
maxlen := int(float64(len(b))*1.365658237309761) + 1
|
||||
answer := make([]byte, 0, maxlen)
|
||||
mod := new(big.Int)
|
||||
for x.Sign() > 0 {
|
||||
// Calculating with big.Int is slow for each iteration.
|
||||
// x, mod = x / 58, x % 58
|
||||
//
|
||||
// Instead we can try to do as much calculations on int64.
|
||||
// x, mod = x / 58^10, x % 58^10
|
||||
//
|
||||
// Which will give us mod, which is 10 digit base58 number.
|
||||
// We'll loop that 10 times to convert to the answer.
|
||||
|
||||
x.DivMod(x, bigRadix10, mod)
|
||||
if x.Sign() == 0 {
|
||||
// When x = 0, we need to ensure we don't add any extra zeros.
|
||||
m := mod.Int64()
|
||||
for m > 0 {
|
||||
answer = append(answer, alphabet[m%58])
|
||||
m /= 58
|
||||
}
|
||||
} else {
|
||||
m := mod.Int64()
|
||||
for i := 0; i < 10; i++ {
|
||||
answer = append(answer, alphabet[m%58])
|
||||
m /= 58
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// leading zero bytes
|
||||
for _, i := range b {
|
||||
if i != 0 {
|
||||
break
|
||||
}
|
||||
answer = append(answer, alphabetIdx0)
|
||||
}
|
||||
|
||||
// reverse
|
||||
alen := len(answer)
|
||||
for i := 0; i < alen/2; i++ {
|
||||
answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
|
||||
}
|
||||
|
||||
return string(answer)
|
||||
}
|
||||
52
vendor/maunium.net/go/mautrix/util/base58/base58check.go
generated
vendored
Normal file
52
vendor/maunium.net/go/mautrix/util/base58/base58check.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package base58
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrChecksum indicates that the checksum of a check-encoded string does not verify against
|
||||
// the checksum.
|
||||
var ErrChecksum = errors.New("checksum error")
|
||||
|
||||
// ErrInvalidFormat indicates that the check-encoded string has an invalid format.
|
||||
var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing")
|
||||
|
||||
// checksum: first four bytes of sha256^2
|
||||
func checksum(input []byte) (cksum [4]byte) {
|
||||
h := sha256.Sum256(input)
|
||||
h2 := sha256.Sum256(h[:])
|
||||
copy(cksum[:], h2[:4])
|
||||
return
|
||||
}
|
||||
|
||||
// CheckEncode prepends a version byte and appends a four byte checksum.
|
||||
func CheckEncode(input []byte, version byte) string {
|
||||
b := make([]byte, 0, 1+len(input)+4)
|
||||
b = append(b, version)
|
||||
b = append(b, input...)
|
||||
cksum := checksum(b)
|
||||
b = append(b, cksum[:]...)
|
||||
return Encode(b)
|
||||
}
|
||||
|
||||
// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
|
||||
func CheckDecode(input string) (result []byte, version byte, err error) {
|
||||
decoded := Decode(input)
|
||||
if len(decoded) < 5 {
|
||||
return nil, 0, ErrInvalidFormat
|
||||
}
|
||||
version = decoded[0]
|
||||
var cksum [4]byte
|
||||
copy(cksum[:], decoded[len(decoded)-4:])
|
||||
if checksum(decoded[:len(decoded)-4]) != cksum {
|
||||
return nil, 0, ErrChecksum
|
||||
}
|
||||
payload := decoded[1 : len(decoded)-4]
|
||||
result = append(result, payload...)
|
||||
return
|
||||
}
|
||||
29
vendor/maunium.net/go/mautrix/util/base58/doc.go
generated
vendored
Normal file
29
vendor/maunium.net/go/mautrix/util/base58/doc.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package base58 provides an API for working with modified base58 and Base58Check
|
||||
encodings.
|
||||
|
||||
# Modified Base58 Encoding
|
||||
|
||||
Standard base58 encoding is similar to standard base64 encoding except, as the
|
||||
name implies, it uses a 58 character alphabet which results in an alphanumeric
|
||||
string and allows some characters which are problematic for humans to be
|
||||
excluded. Due to this, there can be various base58 alphabets.
|
||||
|
||||
The modified base58 alphabet used by Bitcoin, and hence this package, omits the
|
||||
0, O, I, and l characters that look the same in many fonts and are therefore
|
||||
hard to humans to distinguish.
|
||||
|
||||
# Base58Check Encoding Scheme
|
||||
|
||||
The Base58Check encoding scheme is primarily used for Bitcoin addresses at the
|
||||
time of this writing, however it can be used to generically encode arbitrary
|
||||
byte arrays into human-readable strings along with a version byte that can be
|
||||
used to differentiate the same payload. For Bitcoin addresses, the extra
|
||||
version is used to differentiate the network of otherwise identical public keys
|
||||
which helps prevent using an address intended for one network on another.
|
||||
*/
|
||||
package base58
|
||||
33
vendor/maunium.net/go/mautrix/util/dualerror.go
generated
vendored
Normal file
33
vendor/maunium.net/go/mautrix/util/dualerror.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type DualError struct {
|
||||
High error
|
||||
Low error
|
||||
}
|
||||
|
||||
func NewDualError(high, low error) DualError {
|
||||
return DualError{high, low}
|
||||
}
|
||||
|
||||
func (err DualError) Is(other error) bool {
|
||||
return errors.Is(other, err.High) || errors.Is(other, err.Low)
|
||||
}
|
||||
|
||||
func (err DualError) Unwrap() error {
|
||||
return err.Low
|
||||
}
|
||||
|
||||
func (err DualError) Error() string {
|
||||
return fmt.Sprintf("%v: %v", err.High, err.Low)
|
||||
}
|
||||
31
vendor/maunium.net/go/mautrix/util/gjson.go
generated
vendored
Normal file
31
vendor/maunium.net/go/mautrix/util/gjson.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var GJSONEscaper = strings.NewReplacer(
|
||||
`\`, `\\`,
|
||||
".", `\.`,
|
||||
"|", `\|`,
|
||||
"#", `\#`,
|
||||
"@", `\@`,
|
||||
"*", `\*`,
|
||||
"?", `\?`)
|
||||
|
||||
func GJSONPath(path ...string) string {
|
||||
var result strings.Builder
|
||||
for i, part := range path {
|
||||
_, _ = GJSONEscaper.WriteString(&result, part)
|
||||
if i < len(path)-1 {
|
||||
result.WriteRune('.')
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
86
vendor/maunium.net/go/mautrix/util/jsontime/jsontime.go
generated
vendored
Normal file
86
vendor/maunium.net/go/mautrix/util/jsontime/jsontime.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package jsontime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
func UM(time time.Time) UnixMilli {
|
||||
return UnixMilli{Time: time}
|
||||
}
|
||||
|
||||
func UMInt(ts int64) UnixMilli {
|
||||
return UM(time.UnixMilli(ts))
|
||||
}
|
||||
|
||||
func UnixMilliNow() UnixMilli {
|
||||
return UM(time.Now())
|
||||
}
|
||||
|
||||
type UnixMilli struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (um UnixMilli) MarshalJSON() ([]byte, error) {
|
||||
if um.IsZero() {
|
||||
return []byte{'0'}, nil
|
||||
}
|
||||
return json.Marshal(um.UnixMilli())
|
||||
}
|
||||
|
||||
func (um *UnixMilli) UnmarshalJSON(data []byte) error {
|
||||
var val int64
|
||||
err := json.Unmarshal(data, &val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val == 0 {
|
||||
um.Time = time.Time{}
|
||||
} else {
|
||||
um.Time = time.UnixMilli(val)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func U(time time.Time) Unix {
|
||||
return Unix{Time: time}
|
||||
}
|
||||
|
||||
func UInt(ts int64) Unix {
|
||||
return U(time.Unix(ts, 0))
|
||||
}
|
||||
|
||||
func UnixNow() Unix {
|
||||
return U(time.Now())
|
||||
}
|
||||
|
||||
type Unix struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (u Unix) MarshalJSON() ([]byte, error) {
|
||||
if u.IsZero() {
|
||||
return []byte{'0'}, nil
|
||||
}
|
||||
return json.Marshal(u.Unix())
|
||||
}
|
||||
|
||||
func (u *Unix) UnmarshalJSON(data []byte) error {
|
||||
var val int64
|
||||
err := json.Unmarshal(data, &val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val == 0 {
|
||||
u.Time = time.Time{}
|
||||
} else {
|
||||
u.Time = time.Unix(val, 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
30
vendor/maunium.net/go/mautrix/util/marshal.go
generated
vendored
Normal file
30
vendor/maunium.net/go/mautrix/util/marshal.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
// MarshalAndDeleteEmpty marshals a JSON object, then uses gjson to delete empty objects at the given gjson paths.
|
||||
//
|
||||
// This can be used as a convenient way to create a marshaler that omits empty non-pointer structs.
|
||||
// See mautrix.RespSync for example.
|
||||
func MarshalAndDeleteEmpty(marshalable interface{}, paths []string) ([]byte, error) {
|
||||
data, err := json.Marshal(marshalable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, path := range paths {
|
||||
res := gjson.GetBytes(data, path)
|
||||
if res.IsObject() && len(res.Raw) == 2 {
|
||||
data, err = sjson.DeleteBytes(data, path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to delete empty %s: %w", path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
50
vendor/maunium.net/go/mautrix/util/mimetypes.go
generated
vendored
Normal file
50
vendor/maunium.net/go/mautrix/util/mimetypes.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2022 Sumner Evans
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MimeExtensionSanityOverrides includes extensions for various common mimetypes.
|
||||
//
|
||||
// This is necessary because sometimes the OS mimetype database and Go interact in weird ways,
|
||||
// which causes very obscure extensions to be first in the array for common mimetypes
|
||||
// (e.g. image/jpeg -> .jpe, text/plain -> ,v).
|
||||
var MimeExtensionSanityOverrides = map[string]string{
|
||||
"image/png": ".png",
|
||||
"image/webp": ".webp",
|
||||
"image/jpeg": ".jpg",
|
||||
"image/tiff": ".tiff",
|
||||
"image/heif": ".heic",
|
||||
"image/heic": ".heic",
|
||||
|
||||
"audio/mpeg": ".mp3",
|
||||
"audio/ogg": ".ogg",
|
||||
"audio/webm": ".webm",
|
||||
"audio/x-caf": ".caf",
|
||||
"video/mp4": ".mp4",
|
||||
"video/mpeg": ".mpeg",
|
||||
"video/webm": ".webm",
|
||||
|
||||
"text/plain": ".txt",
|
||||
"text/html": ".html",
|
||||
|
||||
"application/xml": ".xml",
|
||||
}
|
||||
|
||||
func ExtensionFromMimetype(mimetype string) string {
|
||||
ext, ok := MimeExtensionSanityOverrides[strings.Split(mimetype, ";")[0]]
|
||||
if !ok {
|
||||
exts, _ := mime.ExtensionsByType(mimetype)
|
||||
if len(exts) > 0 {
|
||||
ext = exts[0]
|
||||
}
|
||||
}
|
||||
return ext
|
||||
}
|
||||
65
vendor/maunium.net/go/mautrix/util/random.go
generated
vendored
Normal file
65
vendor/maunium.net/go/mautrix/util/random.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"hash/crc32"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func RandomBytes(n int) []byte {
|
||||
data := make([]byte, n)
|
||||
_, err := rand.Read(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
var letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
// RandomString generates a random string of the given length.
|
||||
func RandomString(n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
}
|
||||
base64Len := n
|
||||
if n%4 != 0 {
|
||||
base64Len += 4 - (n % 4)
|
||||
}
|
||||
decodedLength := base64.RawStdEncoding.DecodedLen(base64Len)
|
||||
output := make([]byte, base64Len)
|
||||
base64.RawStdEncoding.Encode(output, RandomBytes(decodedLength))
|
||||
for i, char := range output {
|
||||
if char == '+' || char == '/' {
|
||||
_, err := rand.Read(output[i : i+1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
output[i] = letters[int(output[i])%len(letters)]
|
||||
}
|
||||
}
|
||||
return (*(*string)(unsafe.Pointer(&output)))[:n]
|
||||
}
|
||||
|
||||
func base62Encode(val uint32, minWidth int) string {
|
||||
var buf strings.Builder
|
||||
for val > 0 {
|
||||
buf.WriteByte(letters[val%62])
|
||||
val /= 62
|
||||
}
|
||||
return strings.Repeat("0", minWidth-buf.Len()) + buf.String()
|
||||
}
|
||||
|
||||
func RandomToken(namespace string, randomLength int) string {
|
||||
token := namespace + "_" + RandomString(randomLength)
|
||||
checksum := base62Encode(crc32.ChecksumIEEE([]byte(token)), 6)
|
||||
return token + "_" + checksum
|
||||
}
|
||||
23
vendor/maunium.net/go/mautrix/util/returnonce.go
generated
vendored
Normal file
23
vendor/maunium.net/go/mautrix/util/returnonce.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package util
|
||||
|
||||
import "sync"
|
||||
|
||||
// ReturnableOnce is a wrapper for sync.Once that can return a value
|
||||
type ReturnableOnce[Value any] struct {
|
||||
once sync.Once
|
||||
output Value
|
||||
err error
|
||||
}
|
||||
|
||||
func (ronce *ReturnableOnce[Value]) Do(fn func() (Value, error)) (Value, error) {
|
||||
ronce.once.Do(func() {
|
||||
ronce.output, ronce.err = fn()
|
||||
})
|
||||
return ronce.output, ronce.err
|
||||
}
|
||||
77
vendor/maunium.net/go/mautrix/util/ringbuffer.go
generated
vendored
Normal file
77
vendor/maunium.net/go/mautrix/util/ringbuffer.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type pair[Key comparable, Value any] struct {
|
||||
Key Key
|
||||
Value Value
|
||||
}
|
||||
|
||||
type RingBuffer[Key comparable, Value any] struct {
|
||||
ptr int
|
||||
data []pair[Key, Value]
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewRingBuffer[Key comparable, Value any](size int) *RingBuffer[Key, Value] {
|
||||
return &RingBuffer[Key, Value]{
|
||||
data: make([]pair[Key, Value], size),
|
||||
}
|
||||
}
|
||||
|
||||
func (rb *RingBuffer[Key, Value]) Contains(val Key) bool {
|
||||
_, ok := rb.Get(val)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (rb *RingBuffer[Key, Value]) Get(key Key) (val Value, found bool) {
|
||||
rb.lock.RLock()
|
||||
end := rb.ptr
|
||||
for i := clamp(end-1, len(rb.data)); i != end; i = clamp(i-1, len(rb.data)) {
|
||||
if rb.data[i].Key == key {
|
||||
val = rb.data[i].Value
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
rb.lock.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (rb *RingBuffer[Key, Value]) Replace(key Key, val Value) bool {
|
||||
rb.lock.Lock()
|
||||
defer rb.lock.Unlock()
|
||||
end := rb.ptr
|
||||
for i := clamp(end-1, len(rb.data)); i != end; i = clamp(i-1, len(rb.data)) {
|
||||
if rb.data[i].Key == key {
|
||||
rb.data[i].Value = val
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rb *RingBuffer[Key, Value]) Push(key Key, val Value) {
|
||||
rb.lock.Lock()
|
||||
rb.data[rb.ptr] = pair[Key, Value]{Key: key, Value: val}
|
||||
rb.ptr = (rb.ptr + 1) % len(rb.data)
|
||||
rb.lock.Unlock()
|
||||
}
|
||||
|
||||
func clamp(index, len int) int {
|
||||
if index < 0 {
|
||||
return len + index
|
||||
} else if index >= len {
|
||||
return len - index
|
||||
} else {
|
||||
return index
|
||||
}
|
||||
}
|
||||
94
vendor/maunium.net/go/mautrix/util/syncmap.go
generated
vendored
Normal file
94
vendor/maunium.net/go/mautrix/util/syncmap.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package util
|
||||
|
||||
import "sync"
|
||||
|
||||
// SyncMap is a simple map with a built-in mutex.
|
||||
type SyncMap[Key comparable, Value any] struct {
|
||||
data map[Key]Value
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewSyncMap[Key comparable, Value any]() *SyncMap[Key, Value] {
|
||||
return &SyncMap[Key, Value]{
|
||||
data: make(map[Key]Value),
|
||||
}
|
||||
}
|
||||
|
||||
// Set stores a value in the map.
|
||||
func (sm *SyncMap[Key, Value]) Set(key Key, value Value) {
|
||||
sm.Swap(key, value)
|
||||
}
|
||||
|
||||
// Swap sets a value in the map and returns the old value.
|
||||
//
|
||||
// The boolean return parameter is true if the value already existed, false if not.
|
||||
func (sm *SyncMap[Key, Value]) Swap(key Key, value Value) (oldValue Value, wasReplaced bool) {
|
||||
sm.lock.Lock()
|
||||
oldValue, wasReplaced = sm.data[key]
|
||||
sm.data[key] = value
|
||||
sm.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes a key from the map.
|
||||
func (sm *SyncMap[Key, Value]) Delete(key Key) {
|
||||
sm.Pop(key)
|
||||
}
|
||||
|
||||
// Pop removes a key from the map and returns the old value.
|
||||
//
|
||||
// The boolean return parameter is the same as with normal Go map access (true if the key exists, false if not).
|
||||
func (sm *SyncMap[Key, Value]) Pop(key Key) (value Value, ok bool) {
|
||||
sm.lock.Lock()
|
||||
value, ok = sm.data[key]
|
||||
delete(sm.data, key)
|
||||
sm.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get gets a value in the map.
|
||||
//
|
||||
// The boolean return parameter is the same as with normal Go map access (true if the key exists, false if not).
|
||||
func (sm *SyncMap[Key, Value]) Get(key Key) (value Value, ok bool) {
|
||||
sm.lock.RLock()
|
||||
value, ok = sm.data[key]
|
||||
sm.lock.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetOrSet gets a value in the map if the key already exists, otherwise inserts the given value and returns it.
|
||||
//
|
||||
// The boolean return parameter is true if the key already exists, and false if the given value was inserted.
|
||||
func (sm *SyncMap[Key, Value]) GetOrSet(key Key, value Value) (actual Value, wasGet bool) {
|
||||
sm.lock.Lock()
|
||||
defer sm.lock.Unlock()
|
||||
actual, wasGet = sm.data[key]
|
||||
if wasGet {
|
||||
return
|
||||
}
|
||||
sm.data[key] = value
|
||||
actual = value
|
||||
return
|
||||
}
|
||||
|
||||
// Clone returns a copy of the map.
|
||||
func (sm *SyncMap[Key, Value]) Clone() *SyncMap[Key, Value] {
|
||||
return &SyncMap[Key, Value]{data: sm.CopyData()}
|
||||
}
|
||||
|
||||
// CopyData returns a copy of the data in the map as a normal (non-atomic) map.
|
||||
func (sm *SyncMap[Key, Value]) CopyData() map[Key]Value {
|
||||
sm.lock.RLock()
|
||||
copied := make(map[Key]Value, len(sm.data))
|
||||
for key, value := range sm.data {
|
||||
copied[key] = value
|
||||
}
|
||||
sm.lock.RUnlock()
|
||||
return copied
|
||||
}
|
||||
5
vendor/maunium.net/go/mautrix/version.go
generated
vendored
Normal file
5
vendor/maunium.net/go/mautrix/version.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package mautrix
|
||||
|
||||
const Version = "v0.15.0"
|
||||
|
||||
var DefaultUserAgent = "mautrix-go/" + Version
|
||||
153
vendor/maunium.net/go/mautrix/versions.go
generated
vendored
Normal file
153
vendor/maunium.net/go/mautrix/versions.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) 2022 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mautrix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// RespVersions is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientversions
|
||||
type RespVersions struct {
|
||||
Versions []SpecVersion `json:"versions"`
|
||||
UnstableFeatures map[string]bool `json:"unstable_features"`
|
||||
}
|
||||
|
||||
func (versions *RespVersions) ContainsFunc(match func(found SpecVersion) bool) bool {
|
||||
for _, found := range versions.Versions {
|
||||
if match(found) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (versions *RespVersions) Contains(version SpecVersion) bool {
|
||||
return versions.ContainsFunc(func(found SpecVersion) bool {
|
||||
return found == version
|
||||
})
|
||||
}
|
||||
|
||||
func (versions *RespVersions) ContainsGreaterOrEqual(version SpecVersion) bool {
|
||||
return versions.ContainsFunc(func(found SpecVersion) bool {
|
||||
return found.GreaterThan(version) || found == version
|
||||
})
|
||||
}
|
||||
|
||||
func (versions *RespVersions) GetLatest() (latest SpecVersion) {
|
||||
for _, ver := range versions.Versions {
|
||||
if ver.GreaterThan(latest) {
|
||||
latest = ver
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type SpecVersionFormat int
|
||||
|
||||
const (
|
||||
SpecVersionFormatUnknown SpecVersionFormat = iota
|
||||
SpecVersionFormatR
|
||||
SpecVersionFormatV
|
||||
)
|
||||
|
||||
var (
|
||||
SpecR000 = MustParseSpecVersion("r0.0.0")
|
||||
SpecR001 = MustParseSpecVersion("r0.0.1")
|
||||
SpecR010 = MustParseSpecVersion("r0.1.0")
|
||||
SpecR020 = MustParseSpecVersion("r0.2.0")
|
||||
SpecR030 = MustParseSpecVersion("r0.3.0")
|
||||
SpecR040 = MustParseSpecVersion("r0.4.0")
|
||||
SpecR050 = MustParseSpecVersion("r0.5.0")
|
||||
SpecR060 = MustParseSpecVersion("r0.6.0")
|
||||
SpecR061 = MustParseSpecVersion("r0.6.1")
|
||||
SpecV11 = MustParseSpecVersion("v1.1")
|
||||
SpecV12 = MustParseSpecVersion("v1.2")
|
||||
SpecV13 = MustParseSpecVersion("v1.3")
|
||||
SpecV14 = MustParseSpecVersion("v1.4")
|
||||
SpecV15 = MustParseSpecVersion("v1.5")
|
||||
)
|
||||
|
||||
func (svf SpecVersionFormat) String() string {
|
||||
switch svf {
|
||||
case SpecVersionFormatR:
|
||||
return "r"
|
||||
case SpecVersionFormatV:
|
||||
return "v"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type SpecVersion struct {
|
||||
Format SpecVersionFormat
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
|
||||
Raw string
|
||||
}
|
||||
|
||||
var legacyVersionRegex = regexp.MustCompile(`^r(\d+)\.(\d+)\.(\d+)$`)
|
||||
var modernVersionRegex = regexp.MustCompile(`^v(\d+)\.(\d+)$`)
|
||||
|
||||
func MustParseSpecVersion(version string) SpecVersion {
|
||||
sv, err := ParseSpecVersion(version)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sv
|
||||
}
|
||||
|
||||
func ParseSpecVersion(version string) (sv SpecVersion, err error) {
|
||||
sv.Raw = version
|
||||
if parts := modernVersionRegex.FindStringSubmatch(version); parts != nil {
|
||||
sv.Major, _ = strconv.Atoi(parts[1])
|
||||
sv.Minor, _ = strconv.Atoi(parts[2])
|
||||
sv.Format = SpecVersionFormatV
|
||||
} else if parts = legacyVersionRegex.FindStringSubmatch(version); parts != nil {
|
||||
sv.Major, _ = strconv.Atoi(parts[1])
|
||||
sv.Minor, _ = strconv.Atoi(parts[2])
|
||||
sv.Patch, _ = strconv.Atoi(parts[3])
|
||||
sv.Format = SpecVersionFormatR
|
||||
} else {
|
||||
err = fmt.Errorf("version '%s' doesn't match either known syntax", version)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (sv *SpecVersion) UnmarshalText(version []byte) error {
|
||||
*sv, _ = ParseSpecVersion(string(version))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sv *SpecVersion) MarshalText() ([]byte, error) {
|
||||
return []byte(sv.String()), nil
|
||||
}
|
||||
|
||||
func (sv SpecVersion) String() string {
|
||||
switch sv.Format {
|
||||
case SpecVersionFormatR:
|
||||
return fmt.Sprintf("r%d.%d.%d", sv.Major, sv.Minor, sv.Patch)
|
||||
case SpecVersionFormatV:
|
||||
return fmt.Sprintf("v%d.%d", sv.Major, sv.Minor)
|
||||
default:
|
||||
return sv.Raw
|
||||
}
|
||||
}
|
||||
|
||||
func (sv SpecVersion) LessThan(other SpecVersion) bool {
|
||||
return sv != other && !sv.GreaterThan(other)
|
||||
}
|
||||
|
||||
func (sv SpecVersion) GreaterThan(other SpecVersion) bool {
|
||||
return sv.Format > other.Format ||
|
||||
(sv.Format == other.Format && sv.Major > other.Major) ||
|
||||
(sv.Format == other.Format && sv.Major == other.Major && sv.Minor > other.Minor) ||
|
||||
(sv.Format == other.Format && sv.Major == other.Major && sv.Minor == other.Minor && sv.Patch > other.Patch)
|
||||
}
|
||||
Reference in New Issue
Block a user