mirror of
https://github.com/Finb/Bark.git
synced 2025-12-08 21:36:01 +00:00
支持使用快捷指令直接发送推送
This commit is contained in:
parent
bfed640e35
commit
c1a9e0aa78
@ -179,6 +179,7 @@
|
||||
06D69E3E2C1159E200161A35 /* alarm.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F3250B6DD1001561EC /* alarm.caf */; };
|
||||
06D69E3F2C1159E200161A35 /* fanfare.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204FB250B6DD2001561EC /* fanfare.caf */; };
|
||||
06D69E412C11983E00161A35 /* CallProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06D69E402C11983E00161A35 /* CallProcessor.swift */; };
|
||||
06E62C112D670F62004DC82B /* PushToCurrentIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06E62C102D670F5D004DC82B /* PushToCurrentIntent.swift */; };
|
||||
06E944682C06E40600AC86AB /* NotificationContentProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06E944672C06E40600AC86AB /* NotificationContentProcessor.swift */; };
|
||||
06E9446A2C06E4A200AC86AB /* CiphertextProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06E944692C06E4A200AC86AB /* CiphertextProcessor.swift */; };
|
||||
06E9446D2C06FEC900AC86AB /* LevelProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06E9446C2C06FEC900AC86AB /* LevelProcessor.swift */; };
|
||||
@ -193,6 +194,9 @@
|
||||
06EE1FD326843E9300586708 /* BarkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06EE1FD226843E9300586708 /* BarkTests.swift */; };
|
||||
06EEF333291CCFF400CA228A /* CryptoSettingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06EEF332291CCFF400CA228A /* CryptoSettingController.swift */; };
|
||||
06EEF335291CD00000CA228A /* CryptoSettingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06EEF334291CD00000CA228A /* CryptoSettingViewModel.swift */; };
|
||||
06EF49152D682A99008B91D2 /* PushResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06EF49142D682A96008B91D2 /* PushResponse.swift */; };
|
||||
06EF49172D682AC4008B91D2 /* OptionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06EF49162D682AC3008B91D2 /* OptionsProvider.swift */; };
|
||||
06EF49192D682B3B008B91D2 /* PushToOtherIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06EF49182D682B34008B91D2 /* PushToOtherIntent.swift */; };
|
||||
06F08EA429B098DD006AB9CA /* CryptoSettingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EA329B098DD006AB9CA /* CryptoSettingManager.swift */; };
|
||||
06F08EA529B1DDA7006AB9CA /* Algorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061894C629A75BEA00E001C2 /* Algorithm.swift */; };
|
||||
06F08EA729B1DDFE006AB9CA /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */; };
|
||||
@ -391,6 +395,7 @@
|
||||
06CF784421C7A50300A052D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
06CF784B21C7A51200A052D7 /* NotificationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||
06D69E402C11983E00161A35 /* CallProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallProcessor.swift; sourceTree = "<group>"; };
|
||||
06E62C102D670F5D004DC82B /* PushToCurrentIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushToCurrentIntent.swift; sourceTree = "<group>"; };
|
||||
06E944672C06E40600AC86AB /* NotificationContentProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentProcessor.swift; sourceTree = "<group>"; };
|
||||
06E944692C06E4A200AC86AB /* CiphertextProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CiphertextProcessor.swift; sourceTree = "<group>"; };
|
||||
06E9446C2C06FEC900AC86AB /* LevelProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LevelProcessor.swift; sourceTree = "<group>"; };
|
||||
@ -406,6 +411,9 @@
|
||||
06EE1FD426843E9300586708 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
06EEF332291CCFF400CA228A /* CryptoSettingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSettingController.swift; sourceTree = "<group>"; };
|
||||
06EEF334291CD00000CA228A /* CryptoSettingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSettingViewModel.swift; sourceTree = "<group>"; };
|
||||
06EF49142D682A96008B91D2 /* PushResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushResponse.swift; sourceTree = "<group>"; };
|
||||
06EF49162D682AC3008B91D2 /* OptionsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionsProvider.swift; sourceTree = "<group>"; };
|
||||
06EF49182D682B34008B91D2 /* PushToOtherIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushToOtherIntent.swift; sourceTree = "<group>"; };
|
||||
06F08EA329B098DD006AB9CA /* CryptoSettingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSettingManager.swift; sourceTree = "<group>"; };
|
||||
06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+Extension.swift"; sourceTree = "<group>"; };
|
||||
06F08EAB29B1DECD006AB9CA /* NSLocalizedString+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLocalizedString+Extension.swift"; sourceTree = "<group>"; };
|
||||
@ -675,6 +683,7 @@
|
||||
0661A541204FDA4100965E4E /* Bark */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
06EF49132D682A82008B91D2 /* Intents */,
|
||||
0683486A2050F1310024B6DA /* Bark.entitlements */,
|
||||
0661A542204FDA4100965E4E /* AppDelegate.swift */,
|
||||
0661A549204FDA4100965E4E /* Assets.xcassets */,
|
||||
@ -738,6 +747,17 @@
|
||||
path = BarkTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
06EF49132D682A82008B91D2 /* Intents */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
06EF49162D682AC3008B91D2 /* OptionsProvider.swift */,
|
||||
06EF49142D682A96008B91D2 /* PushResponse.swift */,
|
||||
06E62C102D670F5D004DC82B /* PushToCurrentIntent.swift */,
|
||||
06EF49182D682B34008B91D2 /* PushToOtherIntent.swift */,
|
||||
);
|
||||
path = Intents;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
99BD309BDB7F62B5DC0CECE1 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1239,6 +1259,7 @@
|
||||
06AE311C266F54A500B39FBB /* GroupTableViewCell.swift in Sources */,
|
||||
0672CB06256903F700570C9D /* MessageListViewModel.swift in Sources */,
|
||||
0627DABD2990D615002F3F69 /* BorderTextField.swift in Sources */,
|
||||
06E62C112D670F62004DC82B /* PushToCurrentIntent.swift in Sources */,
|
||||
0633E80A256A091B00ED0680 /* MJRefresh+Rx.swift in Sources */,
|
||||
0637FA8C20E0D7A700E80174 /* BaseViewController.swift in Sources */,
|
||||
0642B55C27EB149900453D91 /* MutableTextCellViewModel.swift in Sources */,
|
||||
@ -1286,6 +1307,7 @@
|
||||
0604F7DF20620D4900B32F09 /* ServerManager.swift in Sources */,
|
||||
0667D192247D162C005DE2ED /* MessageTableViewCell.swift in Sources */,
|
||||
06E944762C07013000AC86AB /* RealmConfiguration.swift in Sources */,
|
||||
06EF49152D682A99008B91D2 /* PushResponse.swift in Sources */,
|
||||
0603706720E1E31600F4CA05 /* Defines.swift in Sources */,
|
||||
06787C3B2AB82BDB008ABDD7 /* CrashReportViewController.swift in Sources */,
|
||||
064CAB9E256BE9090018155C /* PreviewCardCellViewModel.swift in Sources */,
|
||||
@ -1308,6 +1330,7 @@
|
||||
06EEF335291CD00000CA228A /* CryptoSettingViewModel.swift in Sources */,
|
||||
0637FA8020E0981E00E80174 /* BarkSettings.swift in Sources */,
|
||||
065BE4402563D649002A8CA4 /* SoundsViewModel.swift in Sources */,
|
||||
06EF49192D682B3B008B91D2 /* PushToOtherIntent.swift in Sources */,
|
||||
0647DB682CE604AA00102066 /* MessageSettingFooter.swift in Sources */,
|
||||
065BE4462563D7E5002A8CA4 /* ViewModelType.swift in Sources */,
|
||||
0603706B20E20A7C00F4CA05 /* String+Extension.swift in Sources */,
|
||||
@ -1318,6 +1341,7 @@
|
||||
06BBB8B72567AC140076F63E /* MessageSettingsViewModel.swift in Sources */,
|
||||
06BBB8C12567B3EF0076F63E /* BaseTableViewCell.swift in Sources */,
|
||||
0661A543204FDA4100965E4E /* AppDelegate.swift in Sources */,
|
||||
06EF49172D682AC4008B91D2 /* OptionsProvider.swift in Sources */,
|
||||
06F08EAF29B5D9FF006AB9CA /* HUD.swift in Sources */,
|
||||
065AE76B2987777F00323230 /* ArchiveSettingRelay.swift in Sources */,
|
||||
06F08EAC29B1DECD006AB9CA /* NSLocalizedString+Extension.swift in Sources */,
|
||||
|
||||
64
Bark/Intents/OptionsProvider.swift
Normal file
64
Bark/Intents/OptionsProvider.swift
Normal file
@ -0,0 +1,64 @@
|
||||
//
|
||||
// OptionsProvider.swift
|
||||
// Bark
|
||||
//
|
||||
// Created by huangfeng on 2/21/25.
|
||||
// Copyright © 2025 Fin. All rights reserved.
|
||||
//
|
||||
import AppIntents
|
||||
|
||||
@available(iOS 16, *)
|
||||
struct ServerAddressOptionsProvider: DynamicOptionsProvider {
|
||||
func results() async throws -> [String] {
|
||||
return ServerManager.shared.servers.map { server in
|
||||
return server.address + "/" + server.key
|
||||
}
|
||||
}
|
||||
|
||||
func defaultResult() async -> String? {
|
||||
return ServerManager.shared.currentServer.address + "/" + ServerManager.shared.currentServer.key
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 16, *)
|
||||
struct SoundOptionsProvider: DynamicOptionsProvider {
|
||||
func results() async throws -> [String] {
|
||||
var customSounds: [String] = []
|
||||
if let soundsDirectoryUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.bark")?.appendingPathComponent("Library/Sounds").path {
|
||||
customSounds = getSounds(urls: getFilesInDirectory(directory: soundsDirectoryUrl, suffix: "caf"))
|
||||
}
|
||||
let defaultSounds = getSounds(urls: Bundle.main.urls(forResourcesWithExtension: "caf", subdirectory: nil) ?? [])
|
||||
|
||||
return customSounds + defaultSounds
|
||||
}
|
||||
|
||||
func getSounds(urls: [URL]) -> [String] {
|
||||
let urls = urls.sorted { u1, u2 -> Bool in
|
||||
u1.lastPathComponent.localizedStandardCompare(u2.lastPathComponent) == ComparisonResult.orderedAscending
|
||||
}
|
||||
return urls.map { $0.deletingPathExtension().lastPathComponent }
|
||||
}
|
||||
|
||||
func getFilesInDirectory(directory: String, suffix: String) -> [URL] {
|
||||
let fileManager = FileManager.default
|
||||
do {
|
||||
let files = try fileManager.contentsOfDirectory(atPath: directory)
|
||||
return files.compactMap { file -> URL? in
|
||||
if file.hasSuffix(suffix), !file.hasPrefix(kBarkSoundPrefix) {
|
||||
// 不要包含 kBarkSoundPrefix 开头的,这些是为了 call=1 合成的 30s 长铃声,不算用户上传的
|
||||
return URL(fileURLWithPath: directory).appendingPathComponent(file)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 16, *)
|
||||
struct VolumeOptionsProvider: DynamicOptionsProvider {
|
||||
func results() async throws -> [Int] {
|
||||
return Array(0...10)
|
||||
}
|
||||
}
|
||||
12
Bark/Intents/PushResponse.swift
Normal file
12
Bark/Intents/PushResponse.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// PushResponse.swift
|
||||
// Bark
|
||||
//
|
||||
// Created by huangfeng on 2/21/25.
|
||||
// Copyright © 2025 Fin. All rights reserved.
|
||||
//
|
||||
|
||||
struct PushResponse: Decodable {
|
||||
let message: String?
|
||||
let code: Int?
|
||||
}
|
||||
86
Bark/Intents/PushToCurrentIntent.swift
Normal file
86
Bark/Intents/PushToCurrentIntent.swift
Normal file
@ -0,0 +1,86 @@
|
||||
//
|
||||
// Intents.swift
|
||||
// Bark
|
||||
//
|
||||
// Created by huangfeng on 2/20/25.
|
||||
// Copyright © 2025 Fin. All rights reserved.
|
||||
//
|
||||
|
||||
import Alamofire
|
||||
import AppIntents
|
||||
|
||||
@available(iOS 16, *)
|
||||
struct PushToCurrentIntent: AppIntent {
|
||||
static var title: LocalizedStringResource = "sendPushNotification"
|
||||
static var openAppWhenRun: Bool = false
|
||||
|
||||
@Parameter(title: "ServerAddress", optionsProvider: ServerAddressOptionsProvider())
|
||||
var address: String
|
||||
|
||||
@Parameter(title: "CustomedNotificationTitle")
|
||||
var title: String?
|
||||
@Parameter(title: "CustomedNotificationContent")
|
||||
var body: String?
|
||||
|
||||
@Parameter(title: "ringtone")
|
||||
var isCall: Bool
|
||||
|
||||
@Parameter(title: "criticalAlert")
|
||||
var isCritical: Bool
|
||||
|
||||
@Parameter(title: "ringtoneVolume", optionsProvider: VolumeOptionsProvider())
|
||||
var volume: Int?
|
||||
|
||||
@Parameter(title: "notificationSound", optionsProvider: SoundOptionsProvider())
|
||||
var sound: String?
|
||||
|
||||
@Parameter(title: "notificationIcon")
|
||||
var icon: URL?
|
||||
|
||||
@Parameter(title: "group")
|
||||
var group: String?
|
||||
|
||||
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
|
||||
let url = ServerManager.shared.currentServer.address + "/\(ServerManager.shared.currentServer.key)"
|
||||
|
||||
var params: [String: Any] = [:]
|
||||
|
||||
if let title {
|
||||
params["title"] = title
|
||||
}
|
||||
if let body {
|
||||
params["body"] = body
|
||||
}
|
||||
if title == nil, body == nil {
|
||||
params["body"] = "Empty Notification"
|
||||
}
|
||||
if isCritical {
|
||||
params["level"] = "critical"
|
||||
}
|
||||
if let volume {
|
||||
params["volume"] = volume
|
||||
}
|
||||
if isCall {
|
||||
params["call"] = 1
|
||||
}
|
||||
if let sound {
|
||||
params["sound"] = sound
|
||||
}
|
||||
if let icon {
|
||||
params["icon"] = icon.absoluteString
|
||||
}
|
||||
if let group {
|
||||
params["group"] = group
|
||||
}
|
||||
|
||||
let response = await AF.request(url, method: .post, parameters: params, encoding: JSONEncoding.default)
|
||||
.serializingDecodable(PushResponse.self)
|
||||
.response
|
||||
|
||||
// 打印返回的body
|
||||
if response.response?.statusCode != 200 {
|
||||
return .result(value: false)
|
||||
}
|
||||
return .result(value: true)
|
||||
}
|
||||
}
|
||||
85
Bark/Intents/PushToOtherIntent.swift
Normal file
85
Bark/Intents/PushToOtherIntent.swift
Normal file
@ -0,0 +1,85 @@
|
||||
//
|
||||
// PushToOtherIntent.swift
|
||||
// Bark
|
||||
//
|
||||
// Created by huangfeng on 2/21/25.
|
||||
// Copyright © 2025 Fin. All rights reserved.
|
||||
//
|
||||
import Alamofire
|
||||
import AppIntents
|
||||
|
||||
@available(iOS 16, *)
|
||||
struct PushToOtherIntent: AppIntent {
|
||||
static var title: LocalizedStringResource = "sendPushNotificationToOther"
|
||||
static var openAppWhenRun: Bool = false
|
||||
|
||||
@Parameter(title: "ServerAddress")
|
||||
var address: String
|
||||
|
||||
@Parameter(title: "CustomedNotificationTitle")
|
||||
var title: String?
|
||||
@Parameter(title: "CustomedNotificationContent")
|
||||
var body: String?
|
||||
|
||||
@Parameter(title: "ringtone")
|
||||
var isCall: Bool
|
||||
|
||||
@Parameter(title: "criticalAlert")
|
||||
var isCritical: Bool
|
||||
|
||||
@Parameter(title: "ringtoneVolume", optionsProvider: VolumeOptionsProvider())
|
||||
var volume: Int?
|
||||
|
||||
@Parameter(title: "notificationSound", optionsProvider: SoundOptionsProvider())
|
||||
var sound: String?
|
||||
|
||||
@Parameter(title: "notificationIcon")
|
||||
var icon: URL?
|
||||
|
||||
@Parameter(title: "group")
|
||||
var group: String?
|
||||
|
||||
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
|
||||
let url = ServerManager.shared.currentServer.address + "/\(ServerManager.shared.currentServer.key)"
|
||||
|
||||
var params: [String: Any] = [:]
|
||||
|
||||
if let title {
|
||||
params["title"] = title
|
||||
}
|
||||
if let body {
|
||||
params["body"] = body
|
||||
}
|
||||
if title == nil, body == nil {
|
||||
params["body"] = "Empty Notification"
|
||||
}
|
||||
if isCritical {
|
||||
params["level"] = "critical"
|
||||
}
|
||||
if let volume {
|
||||
params["volume"] = volume
|
||||
}
|
||||
if isCall {
|
||||
params["call"] = 1
|
||||
}
|
||||
if let sound {
|
||||
params["sound"] = sound
|
||||
}
|
||||
if let icon {
|
||||
params["icon"] = icon.absoluteString
|
||||
}
|
||||
if let group {
|
||||
params["group"] = group
|
||||
}
|
||||
|
||||
let response = await AF.request(url, method: .post, parameters: params, encoding: JSONEncoding.default)
|
||||
.serializingDecodable(PushResponse.self)
|
||||
.response
|
||||
|
||||
// 打印返回的body
|
||||
if response.response?.statusCode != 200 {
|
||||
return .result(value: false)
|
||||
}
|
||||
return .result(value: true)
|
||||
}
|
||||
}
|
||||
@ -3306,6 +3306,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ringtoneVolume" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Ringtone Volume for Critical Alerts"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "重要な警告の着信音量"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Önemli Uyarılar için Zil Sesi Seviyesi"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "重要警告铃声音量"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"SecureConnection" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@ -3335,6 +3364,62 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendPushNotification" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Send notification to this device"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "このデバイスにプッシュ通知を送信"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Bu cihaza bir bildirim gönder"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "发送推送到此设备"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendPushNotificationToOther" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Send notification to other device"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "他のデバイスにプッシュ通知を送信する"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Diğer cihazlara bildirim gönder"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "发送推送到其他设备"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ServerAddress" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user