2. 실시간 Debugging이 가능하다.
- error가 있으면, 아예 컴파일이 안 된다.
3. 화려하다.
http://suritam9.woweb.net/HelloWorld/bin/HelloWorld.html
[Vision API] 구글 드라이브 이미지 파일 리사이징 후 Vision API 사용
라이브러리 : 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF 로 추가(OAuth2)
고급 구글 서비스 : Drive API
행아웃 채트 API 활성화
CLOUD VISION API 는 이미지를 식별해 주는데, 구글 드라이브에 업로드 된 파일은 경로 참조로 가져오기가 되지 않는다.
base64로 변환 후 사용 가능하다.
function test(){//vision api 테스트
var fileurl="구글 드라이브 이미지 파일 경로";
var data1 = JSON.parse(makeRequest(fileurl));
//data.responses[0].webDetection.bestGuessLabels[0].label, data.responses[0].webDetection.webEntities[0].description 을 추출하여 사용
Logger.log(data1.responses[0].webDetection);
}
function buildJSONRequestImgBase64(val) {//base64 인코딩 및 json 메시지 생성
var file = DriveApp.getFileById(val);
var data1 = Utilities.base64Encode(file.getBlob().getBytes());
return JSON.stringify({
requests: [{
image: {
content:data1
},
features: [{
type: "WEB_DETECTION",
maxResults: 1
}]
}]
});
}
var APIKey = 'Your key';
function makeRequest(b64) {// Make a POST request to Vision API with a JSON payload.
var visionApiUrl = 'https://vision.googleapis.com/v1/images:annotate?key=' + APIKey;
var JSON_REQ = buildJSONRequestImgBase64(b64);
var options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON_REQ
};
var response = UrlFetchApp.fetch(visionApiUrl, options);
return response.getContentText();
}
//행아웃 채팅으로 봇이 받은 이미지를 리사이징하여 저장한다.
function uploadAttachmentToDrive(attachment, folderId, fileName){//행아웃 채팅으로 받은 이미지 파일 처리 구글 드라이브로 업로드 및 썸네일 생성
var resourceName = attachment[0].attachmentDataRef.resourceName;
var blob = "";
var url = "https://chat.googleapis.com/v1/media/" + resourceName + "?alt=media"
var service = getOAuth2Service();
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + service.getAccessToken(),
},
'muteHttpExceptions': true,
});
if (response.getResponseCode() != 200) {
return url;
}
blob = response.getBlob();
var folder = DriveApp.getFolderById(folderId);
var uploadFile = folder.createFile(blob);
uploadFile.setName(fileName);
var width = 800;
var link = Drive.Files.get(uploadFile.getId()).thumbnailLink.replace(/\=s.+/, "=s" + width);
var blob2 = UrlFetchApp.fetch(link).getBlob().setName(fileName+"_t");
var file = folder.createFile(blob2);
return file.getId();
}
function onMessage(event) {
if(event.message.attachment != null){
var fileurl = uploadAttachmentToDrive(event.message.attachment,"폴더명",Utilities.formatDate(dt, "GMT+9", "YYYY.MM.dd HH:mm:ss"));
}
function getOAuth2Service() {
var serviceAccountPriveKey = '-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----\n';
var serviceAccountClientEmail = '~gserviceaccount.com';
var scope = 'https://www.googleapis.com/auth/chat.bot';
var service = OAuth2.createService('Vote bot')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(serviceAccountPriveKey)
.setClientId(serviceAccountClientEmail)
.setScope(scope);
if (!service.hasAccess()) {
console.log('Authentication error: %s', service.getLastError());
return;
}
return service;
}
gzip request and non-gzip request
// HTTP POST request
public String sendPost(String targetUrl, String parameters) throws Exception {
URL url = new URL(targetUrl);
disableSslVerification();
//HttpURLConnection con = (HttpURLConnection) url.openConnection();
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("charset", "UTF-8");
con.setRequestProperty("Content-Encoding", "gzip");//when gzip
con.setRequestProperty("Accept-Encoding", "gzip");//when gzip
con.setRequestProperty("content-type", "application/x-www-form-urlencoded");
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.write(compress(parameters));
wr.flush();
wr.close();
//non-gzip request
/*OutputStreamWriter outStream = new OutputStreamWriter(con.getOutputStream(), "UTF-8");
PrintWriter writer = new PrintWriter(outStream);
writer.write(parameters.toString());
writer.flush();*/
BufferedReader in = new BufferedReader(new InputStreamReader(new GZIPInputStream(con.getInputStream()), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
return response.toString();
}
[notion] API를 사용하여 데이터베이스의 목록 출력
여러 설명페이지에 데이터베이스 기본 정보를 출력하는 예제는 많으나, 목록을 출력하는 예제는 부족하다.
query라는 api를 이용하여 POST 방식으로 호출해야 한다.
데이터의 내용이 있을 경우 results의 배열로 리턴되며, 속성 중 column(여기서는 column)를 추가하여 존재하는 목록을 출력해 보았다.
[R] AWS에 도커로 Shiny server 설치 후 googleAuthR 을 이용
AWS t2.micro 에서 R을 이용한 shiny 패키지 설치가 잘 되지 않는다.
그래서 shiny server를 설치하더라도 3838포트는 접속 되지만 shiny app 은 실행시킬 수 없다.
sudo snap install docker
sudo docker pull rocker/shiny-verse
도커 설치 후 실행한 뒤 (여기까지는 https://wikidocs.net/66611 사이트 참고)
sudo docker container run -d -p 3838:3838 rocker/shiny-verse
sudo docker container ls
sudo docker exec -it 컨테이너명 or ID /bin/bash
쉘로 들어간 다음에
R 을 실행시키고 명령 프롬프트에서 install.packages("googleAuthR") 으로 googleAuthR 패키지를 설치한다.
그 후 home 폴더 하위에 디렉터리를 생성하고, app.R 파일을 만들어 소스를 저장한다.
기존 도커를 종료 시키고 저장한 앱을 실행시키면 추가 패키지로 인한 오류 때문에 실행되지 않은 앱들의 문제를 해결 할 수 있다.
sudo docker container stop 컨테이너명 or ID
sudo docker run --rm -d -p 3838:3838 -v /home/ubuntu/[폴더명]:/srv/shiny-server/[앱이름] rocker/shiny-verse
[챗봇] 대화형 질의 응답 만들기
대화형 챗봇을 통해 문자열 입력시 대답하도록 한다.
채팅방에서 실행하게 되면, @봇이름 대화 형식으로 하고, DM일 경우 문자열을 입력하기만 하면 응답한다.
https://cloud.google.com/blog/products/g-suite/building-a-google-hangouts-chatbot-using-apps-script
[채팅방]
[DM]
투표하기 코드를 조금 고쳐서 스프레드시트에 응답 결과를 저장하도록 했다.
클릭 횟수를 프로세스의 단위로 삼아 1번 질문, 2번 질문, ... n번 질문 후 마지막에 앞으로 돌아가도록 했다.
이 설문의 문제는 스프레드시트 쓰기 권한이 oAuth 로 승인되어야 해서 스크립트를 실행하여 권한을 얻어야 하는 문제가 있다.
배포된 스크립트로 하는게 아닌 메니페스토에서 배포라서 스크립트를 작성한 사용자의 권한을 대행하지 못하는 것 같다.
권한 문제가 있어 설문으로 활용할 방법에 대해 좀 더 고민해 봐야겠다.
또한 다른 도메인 사용자 일때도 안 되는 것 같다.
글로벌 권한이 있는 계정으로 다시 만들어 테스트 해봐야 겠다.
[TF] 파이썬과 케라스를 이용한 딥러닝/강화학습 주식투자
도서 [파이썬과 케라스를 이용한 딥러닝/강화학습 주식투자] 에 나온 예제를 실행해 봤다.
현재기준으로 아나콘다 3을 설치하면 python 3.8.x 가 설치되며, python path를 설정해야 제대로 실행 가능하다.
기존 2.7, 3.5 가 설치된 환경이라 여러 버전이 섞여서 다양한(?) 에러를 발생시켰다.
시스템 환경변수에 아래를 등록한다.
PYTHONPATH
c:\programdata\anaconda3;c:\programdata\anaconda3\Lib;c:\programdata\anaconda3\DLLs;c:\programdata\anaconda3\libs;c:\programdata\anaconda3\Scripts;c:\programdata\anaconda3\bin
알수 없는 오류 (set_session, get_default_graph())에 대해 대략 아래와 같이 수정하여 아웃풋을 얻었다.
[networks.py]
23 from tensorflow.keras.backend import set_session -> from tensorflow.keras.backend import clear_session
25 graph = tf.get_default_graph() -> graph = tf.compat.v1.get_default_graph()
시간이 지나면 각종 로그와 그림에 대해 이해하게 되겠지.
[Workers] Cloudflare Workers로 서버리스 코딩
Cloudflare 는 https를 쉽게 제공해주는 서비스로 출발하였다.
해당 기능은 라우팅이나 호스팅과 관련이 있는데, 마이크로 서비스를 사용할 수 있는 Workers를 제공한다.
현재는 nodejs 밖에 사용하지 못한다고 하나, aws에서 nodejs를 lambda로 사용해 봤기 때문에 조금은 수월하게 접근할 수 있었다.
간단한 예제들을 통해 실행, json 다루기, json을 html로 표현하기 등을 해보았다.
json 샘플에서는 header를 json으로 클라이언트에 전달하므로 html이 text로 나온다.
그래서 해당 부분만 수정하여 html 표가 그려질 수 있도록 수정했다.
추후에는 실제 API와 연결하여 활용해 보도록 해야겠다.
'content-type': 'text/html;charset=UTF-8',
[notion] worker 캐시 삭제 및 chunk error
브라우저 보안이 강화되어서 인지 어떤 오류인지 몰라도 js 로딩이 안되면서 도메인 연결한 페이지가 나오지 않는다.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
// 캐시 삭제
const cache = caches.default;
await cache.delete(request);
// 캐시 우회 후 새로운 데이터 가져오기
const response = await fetch(request, {
headers: {
'Cache-Control': 'no-cache'
}
});
// 새 데이터를 캐시에 저장
cache.put(request, response.clone());
return response;
}
[nodejs] aws lambda 에서 httprequest with get param
http api 형태로 하여 api gateway를 생성하였을 때,
https://api경로.execute-api.ap-northeast-2.amazonaws.com/default/api명?파라미터명=데이터
[index.js]
const http = require('https')
exports.handler = async (event) => {
return httprequest(event).then((data) => {
const response = {
param: event,
statusCode: 200,
body: JSON.stringify(data),
};
return response;
});
};
function httprequest(event) {
return new Promise((resolve, reject) => {
const options = {
host: '호스트명(url경로)',
path: encodeURI('/[중간경로]?파라미터명='+event.queryStringParameters.파라미터명),
port: 443,
method: 'GET'
};
const req = http.request(options, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error('statusCode=' + res.statusCode));
}
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch(e) {
reject(e);
}
resolve(body);
});
});
req.on('error', (e) => {
reject(e.message);
});
// send the request
req.end();
});
}
[notion] 데이터베이스(페이지) 삭제(아카이빙)
[매핑] 퍼스널 트레이닝과 공격 기법을 매칭해 본다
사이드 레터럴 레이즈 - 레터럴 무브먼트
마운틴 클라이머 - 프리빌리지 에스컬레이션
점핑 잭 - 디스커버리
플랭크 / 크런치 - 퍼시스턴스
버피테스트 - 스캐닝
스쿼트 - 크레덴셜 스터핑
니업 / 푸시업 - 이니셜 엑세스
레그 레이즈 - 임팩트
덤벨 킥백 - 디펜스 이베이전
댓글 달기