요즘 즐겨하는 게임 세븐나이츠2에서 대보물시대라는 쿠폰 이벤트를 진행한다.
forum.netmarble.com/sk2/view/13/746266
[이벤트] 대보물시대 이벤트! 안내 - 세븐나이츠2 공식 포럼
안녕하세요. 새로운 세븐나이츠의 시작! <세븐나이츠2>의 공식 소통왕 GM포넷입니다. 드.디.어! 세븐나이츠2에서도 시작되는 대 보물 시대! 루비, 토파즈, 문스톤, 전설 영...
forum.netmarble.com
무려 쿠폰이 100개 이걸 언제 일일이 입력하고 있을까 .. 그냥 Golang 연습겸 올려보았다.
자 먼저 HTTP Client를 작성하기전 서버가 어떤 메시지를 받는지 부터 알아봐야 한다.
아래 페이지에 접속하자 (Chrom , Edge)
couponview.netmarble.com/coupon/sknightsmmo/1290
대 보물 시대 이벤트 - Netmarble
드디어 시작된 대 보물 시대! 곳곳에 숨겨진 쿠폰을 찾아 입력해보세요!
couponview.netmarble.com
<F12>를 누르면 아래와 같은 화면이 나올것이다.
1번 회원번호입력 (실제 회원번호를 입력해주가 예외처리 방식이 글자길이인지는 모르지만 아무거나 입력하니까 안된다.. )
2번 쿠폰 입력 (얘는 아무거나 입력 가능)
3번 확인 (POPUP 창 뜨면 그냥 확인 눌러서 닫자)
4번 Netwok 탭을 눌러
5번 화면창을 보자 !
위 순서대로 진행하면 최종적으로 5번 화면에 아래와 같이 나올 것이다.
가볍게 apply를 클릭해보자 !
방대한 양이의 데이터가 나오겠지만 우리는 Header 와 Data 부분만 확인 해보자
Header는 가볍게 Post로 요청하며 요청 대상이 어딘지 URL에 나와있다.
쿠폰 실패 했는데 200 ok!! ㅋㅋ 아마 에러 코드는 Response Body에 담겨 있는것 같다.
Body 영역은 From Data의 view source를 클릭하면 위와 같이 Data Source 부분이 나온다.
key=value 형식이며 구분은 &문자로 하는것으로 보여진다.
wordId와 nickname 의 value는 없어 보인다.
물론 더 확인해볼 정보들이 있지만 구현에 있어 문제는 없음으로 넘어가자
세상 제일 간단한 curl로 http request를 전송시 아래와 같다.
$ curl -k -XPOST https://couponview.netmarble.com/coupon/sknightsmmo/1290/apply -d '\
pid=회원코드&channelCode=100&couponCode=쿠폰'
#회원코드와 쿠폰은 본인 회원코드와 사용할 쿠폰을 입력하면 된다.
자 이제 이 내용을 Go로 개발해보자 설명은 코드 인라인 주석으로 확인
package main
import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
const Ip string = "http://couponview.netmarble.com"
const Url string = "/coupon/sknightsmmo/1290/apply"
const ChannelCode string = "100"
const IdListIni string = "./idlist.ini"
const CouponIni string = "./coupon.ini"
// http response body를 읽어오는 함수
func ReadBody(resp *http.Response) []byte {
resBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("ReadAll Fail... err=%v\n", err)
}
defer resp.Body.Close()
return resBody
}
// http resopnse 메시지 구조체
// Golang에서는 TAG 개념을 이용하여 JSON 변환시 필드명을 변경 할 수있다.
type Data struct {
ResultCode string `json:"resultCode,omitempty"`
CouponSetting string `json:"couponSetting,omitempty"`
RewardItem string `json:"rewardItem,omitempty"`
ResultMessage string `json:"resultMessage,omitempty"`
ResultSubMessage string `json:"resultSubMessage,omitempty"`
}
func main() {
var IdList []string
var IdName []string
var CouponList []string
var CouponDesc []string
var FailCnt int = 0
var AlreadyCnt int = 0
var SuccCnt int = 0
// 우리 길드원 분들을 위한 .. IDLIST 파일 읽어오기 위한 파일 OPEN 함수
f1, err := os.Open(IdListIni)
if err != nil {
fmt.Printf("os.Open() Fail... err=%v\n", err)
return
}
// defer 매크로는 본 함수가 return 될 경우 defer가 마지막에 등록된 애들부터 순차적으로 실행 된다.
// STACK
defer f1.Close()
// 파일의 내용을 읽기 위한 Buffer
reader1 := bufio.NewReader(f1)
for {
line, isPrefix, err := reader1.ReadLine()
if isPrefix || err != nil {
break
}
// ID 계정 코드 형식이며 구분자는 공백
str := strings.Split(string(line), " ")
if len(str) != 2 {
fmt.Printf("Conpon INI Read Fail... line=%s\n", line)
continue
}
// 각각 List에 공백을 지운 문자열을 추가
IdName = append(IdName, strings.TrimSpace(str[0]))
IdList = append(IdList, strings.TrimSpace(str[1]))
}
// 쿠폰 리스트을 읽기 위해 파일 오픈
f2, err := os.Open(CouponIni)
if err != nil {
fmt.Printf("os.Open() Fail... err=%v\n", err)
return
}
defer f2.Close()
reader2 := bufio.NewReader(f2)
for {
line, isPrefix, err := reader2.ReadLine()
if isPrefix || err != nil {
break
}
str := strings.Split(string(line), "-")
if len(str) != 2 {
fmt.Printf("Coupon INI Read Fail... line=%s\n", line)
continue
}
CouponList = append(CouponList, strings.TrimSpace(str[0])) // Coupon
CouponDesc = append(CouponDesc, strings.TrimSpace(str[1])) // Description
}
for i := 0; i < len(IdList); i++ {
FailCnt = 0
SuccCnt = 0
AlreadyCnt = 0
// 각각 쿠폰 결과 저장을 위한 파일 생성
f, err := os.OpenFile(IdName[i]+".txt", os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(0644))
if err != nil {
fmt.Printf("OpenFile Fail... err=%v\n", err)
continue
}
fmt.Printf("#### %s Coupon #####\n", IdName[i])
fmt.Fprintf(f, "#### %s Coupon #####\n", IdName[i])
for j := 0; j < len(CouponList); j++ {
// Body 데이터 생성
Body := fmt.Sprintf("pid=%s&channelCode=%s&couponCode=%s&worldId=&nickname=", IdList[i], ChannelCode, CouponList[j])
// string 데이터를 IoReader로 변환
r := strings.NewReader(Body)
// HTTP POST요청 결과는 RESP 객체에 저장 됨
resp, err := http.Post(Ip+Url, "application/x-www-form-urlencoded; charset=UTF-8", r)
if err != nil {
fmt.Printf("http.Post Fail.. err=%v\n", err)
FailCnt++
continue
}
// HTTP 요청 실패
if resp.StatusCode != http.StatusOK {
fmt.Fprintf(f, "HTTP Request Fail\n")
FailCnt++
continue
}
// IoReader를 byte로 변환
resBody := ReadBody(resp)
if len(resBody) == 0 {
fmt.Fprintf(f, "ResBody Fail...\n")
}
// Response 구조체 변수 선언
var data Data
// []byte 형식의 데이터를 구조체에 저장
json.Unmarshal(resBody, &data)
if data.ResultCode == "COUPON_ALREADY_USE" {
fmt.Fprintf(f, "Already Use Coupon [%s]:[%s]\n", CouponList[j], CouponDesc[j])
AlreadyCnt++
continue
}
fmt.Fprintf(f, "Succ Coupon.. Description [%s]:[%s]\n", CouponList[j], CouponDesc[j])
SuccCnt++
}
fmt.Fprintf(f, "\n\nTotal Result\n")
fmt.Fprintf(f, "%10s : %d\n", "Succ", SuccCnt)
fmt.Fprintf(f, "%10s : %d\n", "Fail", FailCnt)
fmt.Fprintf(f, "%10s : %d\n", "Already", AlreadyCnt)
f.Close()
}
os.Exit(0)
}
URL Tree <Path Tree> - 매칭 (0) | 2021.11.05 |
---|---|
URL Tree <Path Tree> - 생성 (0) | 2021.11.05 |
댓글 영역