Long time not working
This commit is contained in:
@@ -27,6 +27,7 @@ public enum GeneralError: Error {
|
||||
case noData
|
||||
|
||||
// MARK: WebSocket
|
||||
case cannotIssueApprovalKey
|
||||
case invalidWebSocket
|
||||
case webSocketError(_ code: String, _ messageCode: String, _ message: String)
|
||||
case notStringButData
|
||||
@@ -36,4 +37,5 @@ public enum GeneralError: Error {
|
||||
case invalidWebSocketData_EncryptionField
|
||||
case invalidWebSocketData_TrIdField
|
||||
case notEnoughWebSocketData
|
||||
case cannotSubscribeWebSocket
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
let SecondsForOneDay: TimeInterval = 60 * 60 * 24
|
||||
|
||||
|
||||
public protocol WebSocket {
|
||||
var isMockAvailable: Bool { get }
|
||||
var domain: String { get }
|
||||
@@ -81,6 +84,30 @@ extension AuthWebSocket {
|
||||
|
||||
public var responseDataLoggable: Bool { true }
|
||||
|
||||
public func newSession() -> URLSession {
|
||||
let config = URLSessionConfiguration.ephemeral
|
||||
config.waitsForConnectivity = false
|
||||
config.timeoutIntervalForRequest = 60 // 60 seconds
|
||||
config.timeoutIntervalForResource = SecondsForOneDay * 7 // 7 days
|
||||
config.allowsCellularAccess = true
|
||||
config.requestCachePolicy = .reloadIgnoringLocalCacheData
|
||||
// config.multipathServiceType = .handover
|
||||
// config.httpShouldSetCookies = true
|
||||
// config.httpCookieAcceptPolicy = .always
|
||||
config.httpMaximumConnectionsPerHost = 1
|
||||
config.networkServiceType = .responsiveData
|
||||
|
||||
let queue = OperationQueue()
|
||||
|
||||
// 6 -1
|
||||
print("\(config.httpMaximumConnectionsPerHost) \(queue.maxConcurrentOperationCount)")
|
||||
|
||||
queue.maxConcurrentOperationCount = 1
|
||||
queue.underlyingQueue = self.queue
|
||||
|
||||
let session = URLSession(configuration: config, delegate: nil, delegateQueue: queue)
|
||||
return session
|
||||
}
|
||||
|
||||
public mutating func connect() async throws {
|
||||
return try await withUnsafeThrowingContinuation { continuation in
|
||||
@@ -126,6 +153,7 @@ extension AuthWebSocket {
|
||||
let request = Domestic.SubscriptionRequest(approvalKey: credential.approvalKey, type: .subscribed, trId: transactionId, trKey: transactionKey)
|
||||
let requestData = try JSONEncoder().encode(request)
|
||||
let requestJson = String(data: requestData, encoding: .utf8)!
|
||||
print(requestJson)
|
||||
|
||||
let stream = AsyncStream<String> { continuation in
|
||||
self.streamContinuation = continuation
|
||||
@@ -154,6 +182,7 @@ extension AuthWebSocket {
|
||||
let request = Domestic.SubscriptionRequest(approvalKey: credential.approvalKey, type: .unsubscribed, trId: transactionId, trKey: transactionKey)
|
||||
let requestData = try JSONEncoder().encode(request)
|
||||
let requestJson = String(data: requestData, encoding: .utf8)!
|
||||
print(requestJson)
|
||||
|
||||
let stream = AsyncStream<String> { continuation in
|
||||
self.streamContinuation = continuation
|
||||
@@ -222,6 +251,10 @@ extension AuthWebSocket {
|
||||
streamContinuation?.yield(string)
|
||||
streamContinuation?.finish()
|
||||
|
||||
case "(null)":
|
||||
streamContinuation?.yield(string)
|
||||
streamContinuation?.finish()
|
||||
|
||||
default:
|
||||
assertionFailure("Unknown trId \(result.header.trId)")
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ extension Domestic {
|
||||
"H0STASP0"
|
||||
}
|
||||
|
||||
public var session: URLSession { newSession() }
|
||||
public var socket: URLSessionWebSocketTask?
|
||||
public var socketDelegate: URLSessionWebSocketDelegate? { event }
|
||||
public var message: String = ""
|
||||
@@ -33,6 +34,7 @@ extension Domestic {
|
||||
public var credential: WebSocketCredential
|
||||
public var delegate: WebSocketDelegate?
|
||||
public let transactionKey: String
|
||||
public var productCode: String { transactionKey }
|
||||
var event: Event!
|
||||
|
||||
public init(credential: WebSocketCredential, productCode: String) {
|
||||
|
||||
@@ -19,9 +19,10 @@ extension Domestic {
|
||||
"/tryitout/\(transactionId)"
|
||||
}
|
||||
public var transactionId: String {
|
||||
"H0STCNI0"
|
||||
credential.isMock ? "H0STCNI9": "H0STCNI0"
|
||||
}
|
||||
|
||||
public var session: URLSession { newSession() }
|
||||
public var socket: URLSessionWebSocketTask?
|
||||
public var socketDelegate: URLSessionWebSocketDelegate? { event }
|
||||
public var message: String = ""
|
||||
@@ -33,6 +34,7 @@ extension Domestic {
|
||||
public var credential: WebSocketCredential
|
||||
public var delegate: WebSocketDelegate?
|
||||
public let transactionKey: String
|
||||
public var htsID: String { transactionKey }
|
||||
var event: Event!
|
||||
|
||||
public init(credential: WebSocketCredential, htsID: String) {
|
||||
|
||||
@@ -22,6 +22,7 @@ extension Domestic {
|
||||
"H0STCNT0"
|
||||
}
|
||||
|
||||
public var session: URLSession { newSession() }
|
||||
public var socket: URLSessionWebSocketTask?
|
||||
public var socketDelegate: URLSessionWebSocketDelegate? { event }
|
||||
public var message: String = ""
|
||||
@@ -33,6 +34,7 @@ extension Domestic {
|
||||
public var credential: WebSocketCredential
|
||||
public var delegate: WebSocketDelegate?
|
||||
public let transactionKey: String
|
||||
public var productCode: String { transactionKey }
|
||||
var event: Event!
|
||||
|
||||
public init(credential: WebSocketCredential, productCode: String) {
|
||||
|
||||
@@ -37,15 +37,12 @@ public class KissProfile {
|
||||
return profile.recent?.isMock
|
||||
}
|
||||
|
||||
public var approvalKey: String? {
|
||||
public var approvalKeys: [ApprovalKey] {
|
||||
profileLock.lock()
|
||||
defer {
|
||||
profileLock.unlock()
|
||||
}
|
||||
guard let expired = profile.recent?.approvalKeyExpired, expired > Date() else {
|
||||
return nil
|
||||
}
|
||||
return profile.recent?.approvalKey
|
||||
return profile.recent?.approvalKeys ?? []
|
||||
}
|
||||
|
||||
public init() {
|
||||
@@ -54,18 +51,18 @@ public class KissProfile {
|
||||
|
||||
func setAccessToken(_ accessToken: String, expired: Date, isMock: Bool) {
|
||||
profileLock.lock()
|
||||
let approvalKey = profile.recent?.approvalKey
|
||||
let approvalKeyExpired = profile.recent?.approvalKeyExpired
|
||||
profile.recent = Recent(isMock: isMock, accessToken: accessToken, accessTokenExpired: expired, approvalKey: approvalKey, approvalKeyExpired: approvalKeyExpired)
|
||||
let keys = profile.recent?.approvalKeys ?? []
|
||||
profile.recent = Recent(isMock: isMock, accessToken: accessToken, accessTokenExpired: expired, approvalKeys: keys)
|
||||
profileLock.unlock()
|
||||
|
||||
saveProfile()
|
||||
}
|
||||
|
||||
func setApprovalKey(_ approvalKey: String, expired: Date) {
|
||||
func addApprovalKey(_ approvalKey: String, expired: Date) {
|
||||
guard let recent = profile.recent else { return }
|
||||
|
||||
profileLock.lock()
|
||||
let recent = profile.recent?.setted(approvalKey: approvalKey, approvalKeyExpired: expired)
|
||||
profile.recent = recent
|
||||
profile.recent = recent.added(approvalKey: approvalKey, approvalKeyExpired: expired)
|
||||
profileLock.unlock()
|
||||
|
||||
saveProfile()
|
||||
@@ -93,15 +90,23 @@ extension KissProfile {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ApprovalKey: Codable {
|
||||
public let key: String
|
||||
public let keyExpired: Date
|
||||
|
||||
public var isExpired: Bool { keyExpired >= Date() }
|
||||
}
|
||||
|
||||
public struct Recent: Codable {
|
||||
public let isMock: Bool
|
||||
public let accessToken: String
|
||||
public let accessTokenExpired: Date
|
||||
public let approvalKey: String?
|
||||
public let approvalKeyExpired: Date?
|
||||
public var approvalKeys: [ApprovalKey]
|
||||
|
||||
func setted(approvalKey: String, approvalKeyExpired: Date) -> Recent {
|
||||
return Recent(isMock: self.isMock, accessToken: self.accessToken, accessTokenExpired: self.accessTokenExpired, approvalKey: approvalKey, approvalKeyExpired: approvalKeyExpired)
|
||||
func added(approvalKey: String, approvalKeyExpired: Date) -> Recent {
|
||||
var keys = self.approvalKeys
|
||||
keys.append(ApprovalKey(key: approvalKey, keyExpired: approvalKeyExpired))
|
||||
return Recent(isMock: self.isMock, accessToken: self.accessToken, accessTokenExpired: self.accessTokenExpired, approvalKeys: keys)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -183,18 +183,13 @@ extension KissAccount {
|
||||
|
||||
|
||||
public func getApprovalKey() async throws -> String {
|
||||
if let approvalKey = approvalKey {
|
||||
return approvalKey
|
||||
}
|
||||
|
||||
return try await withUnsafeThrowingContinuation { continuation in
|
||||
|
||||
let SecondsForOneDay: TimeInterval = 60 * 60 * 24
|
||||
let request = ApprovalKeyAuthRequest(credential: credential)
|
||||
request.query { result in
|
||||
switch result {
|
||||
case .success(let result):
|
||||
self.setApprovalKey(result.approvalKey, expired: Date().addingTimeInterval(SecondsForOneDay - 60))
|
||||
self.addApprovalKey(result.approvalKey, expired: Date().addingTimeInterval(SecondsForOneDay - 60))
|
||||
continuation.resume(returning: result.approvalKey)
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
|
||||
264
KissMeConsole/Sources/KissConsole+WebSocket.swift
Normal file
264
KissMeConsole/Sources/KissConsole+WebSocket.swift
Normal file
@@ -0,0 +1,264 @@
|
||||
//
|
||||
// KissConsole+WebSocket.swift
|
||||
// KissMeConsole
|
||||
//
|
||||
// Created by ened-book-m1 on 2023/09/04.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import KissMe
|
||||
|
||||
|
||||
extension KissConsole {
|
||||
}
|
||||
|
||||
|
||||
public protocol KissAuctionPriceDelegate: AnyObject {
|
||||
func auction(_ auction: KissAuction, contactPrice: Domestic.ContractPrice)
|
||||
func auction(_ auction: KissAuction, askingPrice: Domestic.AskingPrice)
|
||||
}
|
||||
|
||||
|
||||
public protocol KissAutionNoticeDelegate: AnyObject {
|
||||
func auction(_ auction: KissAuction, contactNotice: Domestic.ContractNotice)
|
||||
}
|
||||
|
||||
|
||||
public class KissAuction: NSObject {
|
||||
|
||||
public class Slot {
|
||||
var contractPrice: Domestic.ContractPriceWebSocket!
|
||||
var askingPrice: Domestic.AskingPriceWebSocket!
|
||||
weak var delegate: KissAuctionPriceDelegate?
|
||||
|
||||
var contractPriceReceiver: ContractPriceReceiver!
|
||||
var askingPriceReceiver: AskingPriceReceiver!
|
||||
|
||||
init(delegate: KissAuctionPriceDelegate?) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
func setup(contractPrice: Domestic.ContractPriceWebSocket, askingPrice: Domestic.AskingPriceWebSocket) {
|
||||
self.contractPrice = contractPrice
|
||||
self.askingPrice = askingPrice
|
||||
}
|
||||
|
||||
func cleanup() {
|
||||
contractPriceReceiver.slot = nil
|
||||
contractPriceReceiver.owner = nil
|
||||
askingPriceReceiver.slot = nil
|
||||
askingPriceReceiver.owner = nil
|
||||
}
|
||||
}
|
||||
|
||||
class ContractPriceReceiver {
|
||||
var slot: Slot?
|
||||
weak var owner: KissAuction?
|
||||
|
||||
init(slot: Slot?, owner: KissAuction) {
|
||||
self.slot = slot
|
||||
self.owner = owner
|
||||
}
|
||||
}
|
||||
|
||||
class AskingPriceReceiver {
|
||||
var slot: Slot?
|
||||
weak var owner: KissAuction?
|
||||
|
||||
init(slot: Slot?, owner: KissAuction) {
|
||||
self.slot = slot
|
||||
self.owner = owner
|
||||
}
|
||||
}
|
||||
|
||||
class ContractNoticeReceiver {
|
||||
weak var delegate: KissAutionNoticeDelegate?
|
||||
weak var owner: KissAuction?
|
||||
|
||||
init(delegate: KissAutionNoticeDelegate?, owner: KissAuction) {
|
||||
self.delegate = delegate
|
||||
self.owner = owner
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: ensure thread-safe
|
||||
var slots: [Slot]
|
||||
|
||||
let loggable: Bool
|
||||
let account: KissAccount
|
||||
var contractNotice: Domestic.ContractNoticeWebSocket?
|
||||
var contractNoticeReceiver: ContractNoticeReceiver?
|
||||
|
||||
public init(account: KissAccount, loggable: Bool) {
|
||||
self.slots = [Slot]()
|
||||
self.loggable = loggable
|
||||
self.account = account
|
||||
self.contractNotice = nil
|
||||
self.contractNoticeReceiver = nil
|
||||
}
|
||||
|
||||
|
||||
public func startNotice(delegate: KissAutionNoticeDelegate?) async throws -> Bool {
|
||||
//account.approvalKeys.
|
||||
guard let isMock = account.isMock else {
|
||||
throw GeneralError.invalidAccessToken
|
||||
}
|
||||
let approvalKey = try await account.getApprovalKey()
|
||||
print("notice price key: \(approvalKey)")
|
||||
let credential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
|
||||
|
||||
contractNotice = Domestic.ContractNoticeWebSocket(credential: credential, htsID: account.accountNo)
|
||||
contractNoticeReceiver = ContractNoticeReceiver(delegate: delegate, owner: self)
|
||||
|
||||
try await contractNotice!.connect()
|
||||
let result = try await contractNotice!.subscribe()
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
public func stopNotice() async throws -> Bool {
|
||||
guard var contractNotice = contractNotice else {
|
||||
return false
|
||||
}
|
||||
|
||||
let result = try await contractNotice.unsubscribe()
|
||||
contractNotice.disconnect()
|
||||
contractNoticeReceiver?.delegate = nil
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
public func addSlot(productNo: String, delegate: KissAuctionPriceDelegate?) async throws -> Slot {
|
||||
guard let isMock = account.isMock else {
|
||||
throw GeneralError.invalidAccessToken
|
||||
}
|
||||
|
||||
func createContractPrice() async throws -> Domestic.ContractPriceWebSocket {
|
||||
let approvalKey = try await account.getApprovalKey()
|
||||
print("contract price key: \(approvalKey)")
|
||||
let credential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
|
||||
|
||||
let contractPrice = Domestic.ContractPriceWebSocket(credential: credential, productCode: productNo)
|
||||
return contractPrice
|
||||
}
|
||||
|
||||
func createAskingPrice() async throws -> Domestic.AskingPriceWebSocket {
|
||||
let approvalKey = try await account.getApprovalKey()
|
||||
print("asking price key: \(approvalKey)")
|
||||
let credential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
|
||||
|
||||
let askingPrice = Domestic.AskingPriceWebSocket(credential: credential, productCode: productNo)
|
||||
return askingPrice
|
||||
}
|
||||
|
||||
let slot = Slot(delegate: delegate)
|
||||
slot.askingPriceReceiver = AskingPriceReceiver(slot: slot, owner: self)
|
||||
slot.contractPriceReceiver = ContractPriceReceiver(slot: slot, owner: self)
|
||||
|
||||
var contractPrice = try await createContractPrice()
|
||||
try await contractPrice.connect()
|
||||
let result = try await contractPrice.subscribe()
|
||||
guard result else {
|
||||
throw GeneralError.cannotSubscribeWebSocket
|
||||
}
|
||||
|
||||
var askingPrice = try await createAskingPrice()
|
||||
try await askingPrice.connect()
|
||||
let result2 = try await askingPrice.subscribe()
|
||||
guard result2 else {
|
||||
throw GeneralError.cannotSubscribeWebSocket
|
||||
}
|
||||
|
||||
slot.setup(contractPrice: contractPrice, askingPrice: askingPrice)
|
||||
slots.append(slot)
|
||||
return slot
|
||||
}
|
||||
|
||||
|
||||
public func getSlot(productNo: String) -> Slot? {
|
||||
guard let slot = slots.first(where: { $0.contractPrice.productCode == productNo }) else {
|
||||
return nil
|
||||
}
|
||||
return slot
|
||||
}
|
||||
|
||||
|
||||
public func removeSlot(productNo: String) async throws -> Bool {
|
||||
guard let slotIndex = slots.firstIndex(where: { $0.contractPrice.productCode == productNo }) else {
|
||||
return false
|
||||
}
|
||||
let slot = slots.remove(at: slotIndex)
|
||||
slot.cleanup()
|
||||
return true
|
||||
}
|
||||
|
||||
private func getWebSocketKey() async throws -> KissWebSocketCredential {
|
||||
guard let isMock = account.isMock else {
|
||||
throw GeneralError.invalidAccessToken
|
||||
}
|
||||
|
||||
if try await account.login() {
|
||||
let approvalKey = try await account.getApprovalKey()
|
||||
return KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
|
||||
}
|
||||
throw GeneralError.cannotIssueApprovalKey
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension KissAuction.ContractPriceReceiver: WebSocketDelegate {
|
||||
|
||||
func webSocket(_ webSocket: WebSocket, didPingpong dateTime: String) {
|
||||
}
|
||||
|
||||
func webSocket(_ webSocket: WebSocket, didReceive data: Any) {
|
||||
guard let owner = owner else { return }
|
||||
guard let data = data as? Domestic.WebSocketData else { return }
|
||||
|
||||
switch data {
|
||||
case .contractPrice(let price):
|
||||
slot?.delegate?.auction(owner, contactPrice: price)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension KissAuction.AskingPriceReceiver: WebSocketDelegate {
|
||||
|
||||
func webSocket(_ webSocket: WebSocket, didPingpong dateTime: String) {
|
||||
}
|
||||
|
||||
func webSocket(_ webSocket: WebSocket, didReceive data: Any) {
|
||||
guard let owner = owner else { return }
|
||||
guard let data = data as? Domestic.WebSocketData else { return }
|
||||
|
||||
switch data {
|
||||
case .askingPrice(let price):
|
||||
slot?.delegate?.auction(owner, askingPrice: price)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension KissAuction.ContractNoticeReceiver: WebSocketDelegate {
|
||||
|
||||
func webSocket(_ webSocket: WebSocket, didPingpong dateTime: String) {
|
||||
}
|
||||
|
||||
func webSocket(_ webSocket: WebSocket, didReceive data: Any) {
|
||||
guard let owner = owner else { return }
|
||||
guard let data = data as? Domestic.WebSocketData else { return }
|
||||
|
||||
switch data {
|
||||
case .contractNotice(let notice):
|
||||
delegate?.auction(owner, contactNotice: notice)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,9 @@ class KissConsole: KissMe.ShopContext {
|
||||
case localizeOnOff = "localize"
|
||||
case test = "test"
|
||||
|
||||
// 웹소켓
|
||||
case real = "real"
|
||||
|
||||
// 뉴스
|
||||
case news = "news"
|
||||
case newsAll = "news all"
|
||||
@@ -131,6 +134,8 @@ class KissConsole: KissMe.ShopContext {
|
||||
return false
|
||||
case .test:
|
||||
return true
|
||||
case .real:
|
||||
return true
|
||||
case .news, .newsAll:
|
||||
return false
|
||||
}
|
||||
@@ -277,6 +282,8 @@ class KissConsole: KissMe.ShopContext {
|
||||
case .localizeOnOff: await onLocalizeOnOff(args)
|
||||
case .test: onTest(args)
|
||||
|
||||
case .real: await onReal(args)
|
||||
|
||||
case .news: await onNews(args)
|
||||
case .newsAll: onNewsAll(args)
|
||||
|
||||
@@ -1226,6 +1233,26 @@ extension KissConsole {
|
||||
}
|
||||
|
||||
|
||||
private func onReal(_ args: [String]) async {
|
||||
guard args.count == 2 else {
|
||||
print("Missing PNO and on/off")
|
||||
return
|
||||
}
|
||||
let productNo = args[0]
|
||||
guard let option = OnOff(rawValue: args[1]) else {
|
||||
print("Invalid on/off option")
|
||||
return
|
||||
}
|
||||
switch option {
|
||||
case .on:
|
||||
break
|
||||
case .off:
|
||||
break
|
||||
}
|
||||
print("WebSocket listening \(option.rawValue) for \(productNo)")
|
||||
}
|
||||
|
||||
|
||||
private func onNews(_ args: [String]) async {
|
||||
guard args.count == 1, let day = args[0].yyyyMMdd_toDate else {
|
||||
print("Missing day")
|
||||
|
||||
@@ -14,4 +14,6 @@ import KissMe
|
||||
//test_get_websocket_key_and_contact_price()
|
||||
//test_get_websocket_key_and_asking_price()
|
||||
//test_parse_contact_price_response()
|
||||
test_websocket_dump_data()
|
||||
//test_websocket_dump_data()
|
||||
|
||||
test_auction()
|
||||
|
||||
@@ -9,10 +9,68 @@ import Foundation
|
||||
import KissMe
|
||||
|
||||
|
||||
class DumpWebSocketData: NSObject {
|
||||
class TestAution: NSObject {
|
||||
|
||||
}
|
||||
|
||||
extension TestAution: KissAutionNoticeDelegate {
|
||||
func auction(_ auction: KissAuction, contactNotice: KissMe.Domestic.ContractNotice) {
|
||||
}
|
||||
}
|
||||
|
||||
extension TestAution: KissAuctionPriceDelegate {
|
||||
func auction(_ auction: KissAuction, contactPrice: KissMe.Domestic.ContractPrice) {
|
||||
}
|
||||
|
||||
func auction(_ auction: KissAuction, askingPrice: KissMe.Domestic.AskingPrice) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func test_auction() {
|
||||
let isMock = true
|
||||
let productNo = "065350"
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
guard let account = await test_get_account(isMock: isMock) else {
|
||||
return
|
||||
}
|
||||
|
||||
let auction = KissAuction(account: account, loggable: true)
|
||||
|
||||
let testAuction = TestAution()
|
||||
var success: Bool = false
|
||||
|
||||
do {
|
||||
// success = try await auction.startNotice(delegate: testAuction)
|
||||
// print("startNotice: \(success)")
|
||||
|
||||
let _ = try await auction.addSlot(productNo: productNo, delegate: testAuction)
|
||||
print("addSlot: ok")
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 60)
|
||||
|
||||
success = try await auction.removeSlot(productNo: productNo)
|
||||
print("removeSlot: \(success)")
|
||||
|
||||
|
||||
// success = try await auction.stopNotice()
|
||||
// print("stopNotice: \(success)")
|
||||
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
|
||||
class DumpWebSocketData: NSObject {
|
||||
}
|
||||
|
||||
|
||||
extension DumpWebSocketData: WebSocketDelegate {
|
||||
|
||||
@@ -167,6 +225,29 @@ func test_get_websocket_key_and_contact_price() {
|
||||
}
|
||||
|
||||
|
||||
func test_get_account(isMock: Bool) async -> KissAccount? {
|
||||
let credential: Credential
|
||||
|
||||
do {
|
||||
credential = try KissCredential(isMock: isMock)
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
|
||||
let account = KissAccount(credential: credential)
|
||||
do {
|
||||
if try await account.login() {
|
||||
return account
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func test_get_websocket_key(isMock: Bool) async -> (KissAccount, String)? {
|
||||
let credential: Credential
|
||||
|
||||
@@ -176,15 +257,20 @@ func test_get_websocket_key(isMock: Bool) async -> (KissAccount, String)? {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
let account = KissAccount(credential: credential)
|
||||
do {
|
||||
/// Return existing valid key
|
||||
if let approvalKey = account.approvalKey {
|
||||
return (account, approvalKey)
|
||||
if let approvalKey = account.approvalKeys.first, !approvalKey.isExpired {
|
||||
return (account, approvalKey.key)
|
||||
}
|
||||
|
||||
if try await account.login() {
|
||||
|
||||
if let approvalKey = account.approvalKeys.first, !approvalKey.isExpired {
|
||||
return (account, approvalKey.key)
|
||||
}
|
||||
|
||||
let approvalKey = try await account.getApprovalKey()
|
||||
print("approvalKey : \(approvalKey)")
|
||||
return (account, approvalKey)
|
||||
|
||||
@@ -51,6 +51,7 @@ WIP `showcase` | 추천 상품을 제안함.
|
||||
`hate (탭) (PNO)` | 관심 종목에서 삭제함.
|
||||
`localize names` | csv field name 에 대해서 한글명을 제공하는 **data/localized-names.csv** 를 저장.
|
||||
`localize (on/off)`| 앞으로 저장하는 모든 csv file 의 field 에 (on) 이면 한글명으로, (off) 이면 영문으로 저장.
|
||||
`real (PNO) (on/off)` | 실시간 웹소켓을 접속하여 수신된 데이터를 기록합니다. (on) 이면 파일로 기록, (off) 이면 기록하지 않음.
|
||||
|
||||
* PNO 는 `Product NO` 의 약자이고, 상품의 `단축코드` (shortCode) 와 동일합니다.
|
||||
* ONO 는 `Order NO` 의 약자이고, 고유한 주문번호 입니다.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349843202A242AC900E85B08 /* KissConsole+CSV.swift */; };
|
||||
34D3680D2A280801005E6756 /* KissConsole+Candle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D3680C2A280801005E6756 /* KissConsole+Candle.swift */; };
|
||||
34DA3EA42A9A176B00BB3439 /* test_websocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34DA3EA32A9A176B00BB3439 /* test_websocket.swift */; };
|
||||
34DB3C452AA6071D00B6763E /* KissConsole+WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34DB3C442AA6071D00B6763E /* KissConsole+WebSocket.swift */; };
|
||||
34EC4D1F2A7A7365002F947C /* KissConsole+News.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EC4D1E2A7A7365002F947C /* KissConsole+News.swift */; };
|
||||
34EE76862A1C391B009761D2 /* KissMe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 341F5EDB2A0A8C4600962D48 /* KissMe.framework */; };
|
||||
34EE76872A1C391B009761D2 /* KissMe.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 341F5EDB2A0A8C4600962D48 /* KissMe.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@@ -60,6 +61,7 @@
|
||||
349843202A242AC900E85B08 /* KissConsole+CSV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+CSV.swift"; sourceTree = "<group>"; };
|
||||
34D3680C2A280801005E6756 /* KissConsole+Candle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Candle.swift"; sourceTree = "<group>"; };
|
||||
34DA3EA32A9A176B00BB3439 /* test_websocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = test_websocket.swift; sourceTree = "<group>"; };
|
||||
34DB3C442AA6071D00B6763E /* KissConsole+WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+WebSocket.swift"; sourceTree = "<group>"; };
|
||||
34EC4D1E2A7A7365002F947C /* KissConsole+News.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+News.swift"; sourceTree = "<group>"; };
|
||||
34F190122A4441F00068C697 /* KissConsole+Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Test.swift"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -108,6 +110,7 @@
|
||||
3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */,
|
||||
34EC4D1E2A7A7365002F947C /* KissConsole+News.swift */,
|
||||
34F190122A4441F00068C697 /* KissConsole+Test.swift */,
|
||||
34DB3C442AA6071D00B6763E /* KissConsole+WebSocket.swift */,
|
||||
349327F62A20E3E300097063 /* Foundation+Extensions.swift */,
|
||||
);
|
||||
name = KissMeConsole;
|
||||
@@ -198,6 +201,7 @@
|
||||
341F5F052A13B82F00962D48 /* test.swift in Sources */,
|
||||
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */,
|
||||
348168492A2F92AC00A50BD3 /* KissContext.swift in Sources */,
|
||||
34DB3C452AA6071D00B6763E /* KissConsole+WebSocket.swift in Sources */,
|
||||
3435A7F42A35B4D000D604F1 /* KissConsole+Price.swift in Sources */,
|
||||
34DA3EA42A9A176B00BB3439 /* test_websocket.swift in Sources */,
|
||||
34F190132A4441F00068C697 /* KissConsole+Test.swift in Sources */,
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
viewDebuggingEnabled = "No"
|
||||
consoleMode = "1">
|
||||
consoleMode = "1"
|
||||
structuredConsoleMode = "3">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
|
||||
Reference in New Issue
Block a user